Overview
Backup hooks allow you to run custom scripts at key points in the backup lifecycle:
- Pre-backup: Execute before backup starts
- Post-backup: Execute after backup completes (success, failure, or warning)
Common use cases:
- Stop/start services during backups
- Create database dumps
- Snapshot volumes
- Send custom notifications
- Clean up temporary files
Hook Types
Repository-Level Hooks
Run every time a specific repository is backed up:
- Edit repository settings
- Scroll to Backup Hooks section
- Add scripts to:
- Pre-Backup Script: Runs before backup
- Post-Backup Script: Runs after backup
- Configure timeouts and behavior
- Save changes
Repository hooks run for both manual and scheduled backups of that repository.
Schedule-Level Hooks
Run once per scheduled job, regardless of how many repositories:
- Create or edit a scheduled job
- Enable Run Repository Scripts to execute per-repository hooks
- Add schedule-level scripts:
- Pre-Backup Script: Runs once before all repositories
- Post-Backup Script: Runs once after all repositories
Schedule-level hooks are ideal for global preparation/cleanup tasks, while repository-level hooks handle repository-specific operations.
Script Execution Environment
Available Variables
Hooks have access to environment variables with backup context:
#!/bin/bash
# Repository information
echo "Repository: $BORG_UI_REPO_NAME" # Repository name
echo "Path: $BORG_UI_REPO_PATH" # Repository path
echo "Job ID: $BORG_UI_JOB_ID" # Backup job ID
echo "Hook: $BORG_UI_HOOK_TYPE" # "pre-backup"
Working Directory
Scripts execute inside the Borg UI container:
- Container path:
/home/borg
- Host access: Use
/local/ prefix
- Mounted volumes: As configured in Docker
Permissions
Scripts run as the borg user (UID 1000):
- Full access to
/data (container data volume)
- Access to mounted host directories via
/local/
- Can execute Docker commands if socket is mounted
Do not use sudo in scripts. Mount Docker socket or volumes with appropriate permissions instead.
Common Patterns
Stop/Start Docker Containers
Ensure consistent backups of containerized applications:
#!/bin/bash
set -e
echo "Stopping containers before backup..."
# Stop containers gracefully
docker stop myapp
docker stop database
echo "Containers stopped successfully"
Docker Compose Setup:
services:
borg-ui:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw
user: "1000:999" # 999 is typically docker group
Add the borg user to the docker group inside the container for socket access without root.
Database Dumps
Create consistent database backups:
#!/bin/bash
set -e
DUMP_DIR="/local/backups/mysql-dumps"
mkdir -p "$DUMP_DIR"
echo "Creating MySQL dump..."
# Dump all databases
docker exec mysql-container mysqldump \
-u root \
-p"${MYSQL_ROOT_PASSWORD}" \
--all-databases \
--single-transaction \
--quick \
> "${DUMP_DIR}/all-databases.sql"
echo "MySQL dump created: ${DUMP_DIR}/all-databases.sql"
Post-Backup Cleanup:
#!/bin/bash
echo "Cleaning up database dumps..."
rm -rf /local/backups/mysql-dumps/*
rm -rf /local/backups/postgres-dumps/*
rm -rf /local/backups/mongo-dumps/*
echo "Dumps cleaned up"
LVM Snapshots
Create consistent snapshots of logical volumes:
#!/bin/bash
set -e
VG="vg0"
LV="data"
SNAPSHOT="${LV}_snapshot"
MOUNT="/local/snapshots/${LV}"
echo "Creating LVM snapshot..."
# Create snapshot (10GB snapshot space)
lvcreate -L 10G -s -n "$SNAPSHOT" "/dev/${VG}/${LV}"
# Mount snapshot
mkdir -p "$MOUNT"
mount "/dev/${VG}/${SNAPSHOT}" "$MOUNT"
echo "Snapshot mounted at: $MOUNT"
echo "Backup from snapshot instead of live volume"
Require --privileged or device mapping for LVM access from container.
Custom Notifications
Send notifications beyond Borg UI’s built-in options:
#!/bin/bash
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
if [ "$BORG_UI_BACKUP_STATUS" = "success" ]; then
COLOR="good"
MESSAGE="Backup completed successfully"
elif [ "$BORG_UI_BACKUP_STATUS" = "failure" ]; then
COLOR="danger"
MESSAGE="Backup FAILED"
else
COLOR="warning"
MESSAGE="Backup completed with warnings"
fi
curl -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{
\"attachments\": [{
\"color\": \"$COLOR\",
\"title\": \"Borg UI Backup\",
\"text\": \"$MESSAGE\",
\"fields\": [
{\"title\": \"Repository\", \"value\": \"$BORG_UI_REPO_NAME\", \"short\": true},
{\"title\": \"Status\", \"value\": \"$BORG_UI_BACKUP_STATUS\", \"short\": true}
]
}]
}"
Verify Backup Integrity
Check backup health after completion:
#!/bin/bash
if [ "$BORG_UI_BACKUP_STATUS" = "success" ]; then
echo "Running post-backup verification..."
# Quick integrity check (no data verification)
borg check --repository-only "$BORG_UI_REPO_PATH"
if [ $? -eq 0 ]; then
echo "Repository integrity check passed"
else
echo "WARNING: Repository integrity check failed" >&2
exit 1
fi
fi
Full archive verification (borg check) can take hours on large repositories. Use sparingly or in separate scheduled jobs.
Hook Configuration
Timeout Settings
Control how long hooks can run before being terminated:
- Pre-Hook Timeout: Default 300 seconds (5 minutes)
- Post-Hook Timeout: Default 300 seconds (5 minutes)
Repository Settings
Schedule Settings
- Edit repository
- Scroll to Backup Hooks
- Set Pre-Hook Timeout and Post-Hook Timeout
- Save changes
Schedule-level hooks use the same timeout values as repository hooks.
Increase timeouts for long-running operations like database dumps or snapshots.
Continue on Failure
Control backup behavior when pre-hooks fail:
- Enabled: Backup proceeds even if pre-hook fails
- Disabled: Backup is cancelled on pre-hook failure (default)
When to enable:
- Pre-hook is non-critical (e.g., optional notification)
- Partial hook failure is acceptable
- Want logs from failed hooks
When to disable:
- Pre-hook creates essential state (e.g., database dump)
- Hook failure indicates system problem
- Consistency is critical
Post-hooks always run after backup, regardless of this setting. They see BORG_UI_BACKUP_STATUS to determine success/failure.
Testing Scripts
Before using hooks in production, test them thoroughly:
Test Script Syntax
# Inside container or equivalent environment
bash -n /path/to/script.sh # Check syntax
bash -x /path/to/script.sh # Debug execution
Test with Variables
Simulate the backup environment:
export BORG_UI_REPO_NAME="test-repo"
export BORG_UI_REPO_PATH="/local/backups/test"
export BORG_UI_JOB_ID="123"
export BORG_UI_HOOK_TYPE="pre-backup"
export BORG_UI_BACKUP_STATUS="success"
./test-hook.sh
Use the Test API
Borg UI provides a script testing endpoint:
- Navigate to repository or schedule settings
- Click Test Script button
- Script executes with 30-second timeout
- View stdout, stderr, and exit code
Test API uses sandboxed environment without full Docker access.
Script Library
Borg UI includes a script library for reusable templates:
Create Script
Navigate to Scripts → Library → Create Script.
Write Script
Enter your script with parameterized variables:#!/bin/bash
CONTAINER_NAME="${CONTAINER_NAME}"
docker stop "$CONTAINER_NAME"
Define Parameters
Add parameters for customization:
- Name:
CONTAINER_NAME
- Description: “Docker container to stop”
- Default:
myapp
Use in Repository
When configuring hooks, select script from library and provide parameter values.
Script library promotes reusability across repositories and schedules.
Best Practices
Always use set -e and handle errors gracefully:#!/bin/bash
set -e # Exit on error
set -u # Exit on undefined variable
set -o pipefail # Pipe failures propagate
trap 'echo "Error on line $LINENO" >&2' ERR
Log script actions for debugging:LOG_FILE="/data/logs/backup-hooks.log"
exec 1>> "$LOG_FILE" # Redirect stdout
exec 2>&1 # Redirect stderr to stdout
echo "[$(date)] Starting pre-backup hook for $BORG_UI_REPO_NAME"
Add internal timeouts for external commands:# Timeout command after 60 seconds
timeout 60s docker stop myapp || echo "Timeout stopping container"
Make scripts safe to run multiple times:# Check if container is already stopped
if docker ps | grep -q myapp; then
docker stop myapp
else
echo "Container already stopped"
fi
Store secrets securely, not in scripts:# Use environment variables
DB_PASSWORD="${MYSQL_ROOT_PASSWORD}"
# Or read from file
DB_PASSWORD=$(cat /data/secrets/mysql-password)
Configure in Docker:environment:
- MYSQL_ROOT_PASSWORD=secretpassword
volumes:
- ./secrets:/data/secrets:ro
Troubleshooting
Hook Times Out
- Increase timeout in repository/schedule settings
- Optimize script performance
- Add debug logging to identify slow operations
Hook Fails Silently
Check backup job logs:
- Navigate to Backup Jobs
- Click on the failed job
- View logs for hook stderr output
Docker Commands Fail
Ensure Docker socket is mounted:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw
And user has docker group permissions:
docker exec borg-ui groups
# Should show: borg docker
Permission Denied
For host file access:
# On host
sudo chown -R 1000:1000 /path/to/files
Or mount with appropriate permissions:
volumes:
- /path/to/files:/local/files:rw
Next Steps
Schedule Automated Backups
Use hooks in scheduled backup jobs
Notification Setup
Configure built-in notifications