Sign inSign up

diarmuidk/docker-borg-client

By diarmuidk

Updated 3 months ago

A minimal, generic Docker container for running BorgBackup backups.

Image
Developer tools
Databases & storage
0

3.0K

diarmuidk/docker-borg-client repository overview

Docker Borg Client

Docker Image Version Docker Image Size Docker Pulls Docker Stars CI Tests GitHub release Licence

A minimal, generic Docker container for running BorgBackup backups to any remote SSH-accessible Borg repository. Designed for TrueNAS but works anywhere Docker runs.

Overview

Why Docker Borg Client?

If you're running a home server, NAS, or any system with important data, you need reliable, automated backups. This container solves my backup problem of TrueNAS to a remote Borg:

  • Set it and forget it - Configure once, runs forever. Automated backups on your schedule with smart retention policies that keep recent backups while pruning old ones.
  • Production Upstream technology - Built on BorgBackup, used by thousands for petabytes of data. Provides deduplication, compression, and encryption that can reduce backup sizes by 95%+.
  • Cost-effective - Works with any SSH-accessible storage: a Raspberry Pi at a friend's house, cloud hosting provider, or any VPS. No vendor lock-in.
  • TrueNAS optimized - Built for the TrueNAS ecosystem - but in theory should run on an docker enginer.
  • Data Safety First - Built, tested, and refined on real production data. Features automatic integrity checking, checkpoint-based resumption for interrupted backups, and comprehensive restore verification tools. Your backups are only as good as your ability to restore them.
  • Security by Design - Client-side encryption before data leaves your server. Even if your backup destination is compromised, your data remains encrypted and safe. Zero-knowledge architecture means only you have the keys.

Perfect for:

  • Home lab enthusiasts backing up Docker volumes, databases, and configuration
  • TrueNAS users wanting automated off-site backups of their datasets
  • Small businesses needing GDPR-compliant encrypted backups
  • Anyone who learned the hard way that RAID is not a backup

Table of Contents

Features

  • 🔒 Secure: Client-side encryption with SSH key authentication
  • 🐧 Minimal: Alpine Linux + Borg + SSH client only
  • 🔧 Generic: Works with any Borg-over-SSH target (Hetzner, rsync.net, self-hosted)
  • 📅 Automated: Configurable cron-based scheduled backups
  • ♻️ Smart retention: Automatic pruning with configurable policies
  • 🏗️ Multi-arch: Supports amd64 and arm64

Quick Start

Prerequisites
  1. A remote Borg repository accessible via SSH (see Provider Guides)
  2. SSH key pair for authentication
  3. Docker and Docker Compose installed (or TrueNAS SCALE)
TrueNAS SCALE Setup

TrueNAS SCALE users can deploy this container using the Custom App feature:

  1. Prepare SSH Keys:

    • Create a directory for your SSH keys:
      mkdir -p /mnt/pool/borg-backup/ssh
      ssh-keygen -t ed25519 -f /mnt/pool/borg-backup/ssh/key -N ""
      chmod 700 /mnt/pool/borg-backup/ssh
      chmod 600 /mnt/pool/borg-backup/ssh/key
      
    • Add the public key to your remote backup server:
      cat /mnt/pool/borg-backup/ssh/key.pub
      
  2. Deploy Custom App:

    • Navigate to AppsDiscover AppsCustom App
    • Click Install on Custom App
    • Configure the following:

    Application Name: borg-backup

    Image Configuration:

    • Image Repository: diarmuidk/docker-borg-client
    • Image Tag: latest
    • Image Pull Policy: Always

    Container User and Group (under Advanced Settings):

    • User ID: 0

    • Group ID: 0

    Note: This container runs as root (UID/GID 0:0) to ensure reliable access to all backup paths. Backup containers require broad filesystem access by design. Privileged mode is not required.

    Environment Variables (Required - add all of these):

    BORG_REPO=ssh://[email protected]:22/~/backups
    BORG_PASSPHRASE=your-strong-passphrase-here
    BACKUP_PATHS=/data/dataset1:/data/dataset2
    CRON_SCHEDULE=0 2 * * 0
    PRUNE_KEEP_DAILY=7
    PRUNE_KEEP_WEEKLY=4
    PRUNE_KEEP_MONTHLY=6
    AUTO_INIT=true
    

    Note: Set timezone using TrueNAS's built-in Timezone dropdown (under Advanced Settings), not as an environment variable.

    Optional - Time Window Configuration (for large initial backups):

    BACKUP_WINDOW_START=01:00
    BACKUP_WINDOW_END=07:00
    BACKUP_RATE_LIMIT_IN_WINDOW=-1
    BACKUP_RATE_LIMIT_OUT_WINDOW=0
    

    This configuration runs backups only during 1am-7am at full speed, perfect for large initial backups on limited connections.

    Storage:

    • Add Host Path Volume for SSH keys:

      • Host Path: /mnt/pool/borg-backup/ssh
      • Mount Path: /ssh
      • Read Only: ✅ Enable
    • Add Host Path Volume for each dataset to backup:

      • Host Path: /mnt/pool/your-dataset
      • Mount Path: /data/dataset1
      • Read Only: ✅ Enable
    • Add ixVolume for Borg cache:

      • Mount Path: /borg/cache
    • Add ixVolume for Borg config:

      • Mount Path: /borg/config

    Restart Policy: Unless Stopped

  3. Initialize Repository:

    Option A - Automatic (Recommended):

    • Add AUTO_INIT=true to environment variables
    • Start the app - repository will be automatically initialized on first run
    • Check logs to see initialization message and backup the credentials
    • Navigate to AppsInstalledborg-backupShell
    • Run: cat /borg/config/repo-key.txt and save to password manager

    Option B - Manual:

    • After deployment, access the container shell via TrueNAS web UI
    • Navigate to AppsInstalledborg-backupShell
    • Run: /scripts/init.sh
    • Backup your passphrase and the repository key to password manager
  4. Monitor Backups:

    • View logs: AppsInstalledborg-backupLogs
    • Backups will run automatically according to your cron schedule
TrueNAS-Specific Notes

Timezone Configuration:

  • Do NOT add TZ as a manual environment variable in TrueNAS Custom Apps
  • TrueNAS automatically manages timezone - look for a Timezone dropdown in the app configuration
  • Adding TZ manually will cause deployment errors: Environment variable [TZ] is already defined

SSH Key Permissions:

  • Private key must be 600 (read/write for owner only)
  • SSH directory must be 700 (read/write/execute for owner only)
  • If permissions are incorrect, SSH authentication will silently fail

Testing SSH Connection:

Before deploying, verify SSH key authentication works:

ssh -i /mnt/pool/borg-backup/ssh/key -p <port> [email protected]

If prompted for password, SSH key is not configured correctly on remote server.

Docker Compose Setup
  1. Generate SSH keys (if you don't have them):

    mkdir -p ssh
    ssh-keygen -t ed25519 -f ssh/key -N ""
    
  2. Add the public key to your backup server:

    cat ssh/key.pub
    # Copy this and add it to ~/.ssh/authorized_keys on your backup server
    
  3. Configure environment:

    cp .env.example .env
    # Edit .env with your settings
    
  4. Initialize the Borg repository (one-time):

    docker compose run --rm borg-backup /scripts/init.sh
    
  5. Start the backup container:

    docker compose up -d
    

Configuration Reference

Environment Variables
VariableRequiredDefaultDescription
BORG_REPOYes-Full SSH URL to repository (e.g., ssh://user@host:22/~/backup)
BORG_PASSPHRASEYes-Repository encryption passphrase
BACKUP_PATHSYes-Colon-separated paths to back up (e.g., /data/photos:/data/docs)
BACKUP_EXCLUDESNo-Colon-separated paths/patterns to exclude (e.g., /data/photos/cache:/data/docs/tmp)
BORG_RSHNossh -i /ssh/key -o StrictHostKeyChecking=accept-newSSH command
CRON_SCHEDULENo-Cron expression for scheduled backups (e.g., 0 2 * * 0). Omit to run on-demand only.
RUN_ON_STARTNofalseRun backup immediately on container start
AUTO_INITNofalseAutomatically initialize repository if it doesn't exist
PRUNE_KEEP_DAILYNo7Daily archives to keep
PRUNE_KEEP_WEEKLYNo4Weekly archives to keep
PRUNE_KEEP_MONTHLYNo6Monthly archives to keep
TZNoUTCTimezone for cron jobs
VERIFY_ENABLEDNofalseEnable scheduled repository integrity verification
VERIFY_REPO_CRON_SCHEDULENo-Repository check schedule (e.g., 0 3 * * 0 for weekly Sunday 03:00)
VERIFY_ARCHIVES_CRON_SCHEDULENo-Archives check schedule (e.g., 0 3 1 * * for monthly 1st at 03:00)
VERIFY_LEVELNorepositoryManual verification depth: repository, archives, or full
Notification Variables (Optional)
VariableRequiredDefaultDescription
NOTIFY_TRUENAS_ENABLEDNofalseEnable TrueNAS API notifications
NOTIFY_TRUENAS_API_URLNo-TrueNAS WebSocket URL (e.g., ws://192.168.1.100 or wss://truenas.local)
NOTIFY_TRUENAS_API_KEYNo-TrueNAS API key (generate in Settings → API Keys)
NOTIFY_TRUENAS_VERIFY_SSLNotrueVerify SSL certificates for wss:// (set to false for self-signed)
NOTIFY_EVENTSNobackup.failure,prune.failure,verify.failureComma-separated list of events to notify

Available Events: backup.success, backup.failure, prune.success, prune.failure, verify.success, verify.failure, container.startup, container.shutdown

See TrueNAS API Key Setup Guide for detailed instructions.

Backup Time Window and Rate Limiting (Optional)
VariableRequiredDefaultDescription
BACKUP_WINDOW_STARTNo-Start of backup window in HH:MM format (e.g., 01:00)
BACKUP_WINDOW_ENDNo-End of backup window in HH:MM format (e.g., 07:00)
BACKUP_RATE_LIMIT_IN_WINDOWNo-1Rate limit during window in Mbps (-1 = unlimited)
BACKUP_RATE_LIMIT_OUT_WINDOWNo-1Rate limit outside window in Mbps (0 = stopped, -1 = unlimited)

Rate Limit Values:

  • -1 = Unlimited bandwidth (burst speeds)
  • 0 = Terminate backup outside window, auto-resume from checkpoint (only valid for BACKUP_RATE_LIMIT_OUT_WINDOW)
  • Positive number = Bandwidth limit in Mbps (e.g., 40 = 40 Mbps)

Use Cases:

  1. Large initial backup on limited connection (e.g., 1.5TB on 40 Mbps):

    BACKUP_WINDOW_START=01:00
    BACKUP_WINDOW_END=07:00
    BACKUP_RATE_LIMIT_IN_WINDOW=-1    # Unlimited overnight
    BACKUP_RATE_LIMIT_OUT_WINDOW=0    # Terminated and resumed via checkpoint
    
    • Backup runs at full speed during 1am-7am window
    • Automatically terminated at 7am (checkpoint polling in final 30 min)
    • Resumes next night from last checkpoint
    • Completes 1.5TB in 3-4 nights with minimal rework
  2. Continuous backup with daytime throttle:

    BACKUP_WINDOW_START=22:00
    BACKUP_WINDOW_END=08:00
    BACKUP_RATE_LIMIT_IN_WINDOW=-1    # Unlimited overnight
    BACKUP_RATE_LIMIT_OUT_WINDOW=5    # 5 Mbps trickle during day
    
  3. Daytime backup with bandwidth limit:

    BACKUP_WINDOW_START=09:00
    BACKUP_WINDOW_END=17:00
    BACKUP_RATE_LIMIT_IN_WINDOW=20    # 20 Mbps during business hours
    BACKUP_RATE_LIMIT_OUT_WINDOW=0    # Terminated outside business hours
    

How It Works:

  • Borg (1.1+) automatically creates checkpoints every 30 minutes during backup
  • When backup window ends with BACKUP_RATE_LIMIT_OUT_WINDOW=0:
    • Monitor polls for new checkpoints in final 30 minutes of window
    • If new checkpoint detected → backup terminated immediately (minimizes wasted work)
    • If window ends → backup terminated at deadline (hard stop)
    • Zero out-of-window bandwidth usage (never exceeds window)
  • Next backup run automatically resumes from last checkpoint
  • Container restarts automatically break stale locks and resume from checkpoint
  • No manual intervention required

Requirements:

  • Borg 1.1 or later (for checkpoint support within files)
Repository Integrity Verification (Optional)

Scheduled borg check verification ensures your backup repository remains healthy and detects corruption early. When enabled, two verification jobs are configured automatically:

  • Repository check (weekly) - Fast verification of repository structure
  • Archives check (monthly) - Deeper verification of archive metadata integrity
VariableRequiredDefaultDescription
VERIFY_ENABLEDNofalseEnable scheduled verification
VERIFY_REPO_CRON_SCHEDULENo-Repository check schedule (e.g., 0 3 * * 0 for weekly)
VERIFY_ARCHIVES_CRON_SCHEDULENo-Archives check schedule (e.g., 0 3 1 * * for monthly)

Verification Levels:

LevelCommandSpeedUse Case
repository--repository-onlyFastWeekly scheduled checks - verifies repository structure
archives--archives-onlyMediumMonthly checks - verifies archive metadata integrity
full--verify-dataVery slowManual spot-checks only - reads and verifies all data

Example Configuration:

VERIFY_ENABLED=true
VERIFY_REPO_CRON_SCHEDULE=0 3 * * 0      # Weekly Sunday 3am
VERIFY_ARCHIVES_CRON_SCHEDULE=0 3 1 * *  # Monthly 1st at 3am

Behaviour Notes:

  • Verification breaks any existing borg lock before running - if a backup is in progress, it will be interrupted and resume from checkpoint on next scheduled run
  • Verification is read-only and does not respect backup windows (no bandwidth impact)
  • full level reads all repository data and is very slow on large repos - use for manual spot-checks only
  • No double-runs: If the archives day falls on a repo day (e.g., 1st is a Sunday), only the archives check runs - the repository check is automatically skipped

Manual Verification:

# Quick repository check
docker compose run --rm borg-backup /scripts/verify.sh

# Archives check
docker compose run --rm -e VERIFY_LEVEL=archives borg-backup /scripts/verify.sh

# Full data verification (slow - use for spot checks)
docker compose run --rm -e VERIFY_LEVEL=full borg-backup /scripts/verify.sh
Volume Mounts
Container PathPurposeMode
/dataSource directories to back upread-only
/sshSSH private keyread-only
/borg/cacheBorg cache (improves performance)read-write
/borg/configBorg config persistenceread-write

Manual Operations

TrueNAS SCALE

All manual operations can be performed via the container shell in the TrueNAS web UI:

Access Shell: AppsInstalledborg-backupShell

Then run any of the following commands:

  • List backups: /scripts/restore.sh list
  • View archive info: /scripts/restore.sh info backup-2026-01-18_12-00-00
  • Check repository: /scripts/restore.sh check
  • Verify repository integrity: /scripts/verify.sh
  • Manual backup: /scripts/backup.sh
  • Manual prune: /scripts/prune.sh

Restore from backup:

  1. Create a restore directory on your pool: mkdir -p /mnt/pool/borg-restore
  2. In the TrueNAS web UI, stop the borg-backup app
  3. Edit the app and add a Host Path Volume:
    • Host Path: /mnt/pool/borg-restore
    • Mount Path: /restore
  4. Save and start the app
  5. Access the shell and run: /scripts/restore.sh extract backup-2026-01-18_12-00-00 /restore
  6. Files will be extracted to /mnt/pool/borg-restore on your TrueNAS system
Docker Compose
List Backups
docker compose run --rm borg-backup /scripts/restore.sh list
View Archive Information
docker compose run --rm borg-backup /scripts/restore.sh info backup-2026-01-18_12-00-00
Restore from Backup
# Extract to current directory
docker compose run --rm -v $(pwd)/restore:/restore borg-backup \
  /scripts/restore.sh extract backup-2026-01-18_12-00-00 /restore

# Mount archive for browsing
docker compose run --rm -v $(pwd)/mnt:/mnt borg-backup \
  /scripts/restore.sh mount backup-2026-01-18_12-00-00 /mnt
Check Repository Integrity
docker compose run --rm borg-backup /scripts/restore.sh check
Manual Backup
docker compose run --rm borg-backup /scripts/backup.sh
Manual Prune
docker compose run --rm borg-backup /scripts/prune.sh
Manual Verification
# Default (repository-only) check
docker compose run --rm borg-backup /scripts/verify.sh

# Full data verification (slow)
docker compose run --rm -e VERIFY_LEVEL=full borg-backup /scripts/verify.sh

Docker Compose Example

services:
  borg-backup:
    image: diarmuidk/docker-borg-client:latest
    container_name: borg-backup
    environment:
      - BORG_REPO=ssh://[email protected]:22/~/backups
      - BORG_PASSPHRASE=your-strong-passphrase
      - BACKUP_PATHS=/data/photos:/data/documents
      - CRON_SCHEDULE=0 2 * * 0
      - TZ=Europe/London
    volumes:
      - ./ssh:/ssh:ro
      - /mnt/pool/photos:/data/photos:ro
      - /mnt/pool/documents:/data/documents:ro
      - borg-cache:/borg/cache
      - borg-config:/borg/config
    restart: unless-stopped

volumes:
  borg-cache:
  borg-config:

Additional Guides

Cron Schedule Examples

The CRON_SCHEDULE variable uses standard cron format: minute hour day-of-month month day-of-week

ScheduleDescription
0 2 * * 0Every Sunday at 2am (default)
0 3 * * *Every day at 3am
0 2 * * 1-5Weekdays at 2am
0 */6 * * *Every 6 hours
30 1 1 * *First day of every month at 1:30am

Notifications

Docker Borg Client supports sending notifications to TrueNAS SCALE via the TrueNAS WebSocket JSON-RPC API. This allows you to receive alerts through your existing TrueNAS notification channels (email, Slack, etc.).

Requirements: TrueNAS SCALE 25.04 or later

Quick Setup (TrueNAS SCALE)
  1. Generate API Key in TrueNAS:

    • Navigate to SettingsAPI Keys
    • Click Add and create a new key
    • Copy the generated key (shown only once!)
  2. Configure Notifications:

    NOTIFY_TRUENAS_ENABLED=true
    NOTIFY_TRUENAS_API_URL=ws://192.168.1.100  # Your TrueNAS IP with ws:// protocol
    NOTIFY_TRUENAS_API_KEY=1-abc123yourkey
    NOTIFY_EVENTS=backup.failure,backup.success
    

    Note: Use ws:// for unencrypted WebSocket connections (recommended for local networks).

  3. Test Notification:

    # From container shell
    /scripts/notify.sh "backup.success" "INFO" "Test" "This is a test notification"
    

For detailed setup instructions, see TrueNAS API Key Setup Guide.

Event Types
  • backup.success - Backup completed successfully
  • backup.failure - Backup failed
  • prune.success - Prune completed successfully
  • prune.failure - Prune failed
  • verify.success - Repository verification completed successfully
  • verify.failure - Repository verification failed (potential corruption detected)
  • container.startup - Container started (useful for monitoring container health)
  • container.shutdown - Container stopping (useful for tracking restarts/stops)

Default: Only failures are notified (backup.failure,prune.failure,verify.failure)

Tip: Add container.startup,container.shutdown to track container lifecycle events

Monitoring

View container logs to monitor backup status:

docker compose logs -f borg-backup

Troubleshooting

SSH Connection Issues

Test SSH connection manually:

docker compose run --rm borg-backup ssh -i /ssh/key user@host
Repository Lock

If backup fails due to lock:

docker compose run --rm borg-backup borg break-lock $BORG_REPO
Storage Space

Check repository size:

docker compose run --rm borg-backup borg info $BORG_REPO
Verify Backups

Regularly test restores to ensure backups are working:

docker compose run --rm borg-backup /scripts/restore.sh check

Security Best Practices

  1. Store passphrase securely: Use a password manager or secrets management system
  2. Backup your passphrase: Without it, your backups are unrecoverable
  3. Export repository key: borg key export $BORG_REPO /path/to/keyfile
  4. Restrict SSH key: Use ~/.ssh/authorized_keys restrictions on the backup server
  5. Use read-only mounts: Mount source directories as read-only (:ro)
  6. Regular integrity checks: Run borg check periodically
Data Safety

Your source data is safe: All backup source directories are mounted read-only, so the container cannot modify or delete your original files.

Potentially destructive operations (use with caution):

  • borg break-lock - Only use if you're certain no backup is running
  • borg prune - Deletes old archives according to retention policy (intended behaviour)
  • borg compact - Irreversibly frees space by removing deleted data
  • Deleting /borg/cache or /borg/config - Can corrupt repository metadata

Recommendation: Test your restore process regularly to ensure backups are working correctly.

Disaster Recovery

Understanding how Borg encryption works is critical for disaster recovery planning.

How Borg Encryption Works

Borg uses a two-layer encryption system:

  1. Repository Key: The actual encryption key that encrypts your data

    • Automatically generated during borg init
    • In "repokey" mode (default), stored inside the repository on the remote server
    • Encrypted with your passphrase
  2. Passphrase: The password you set via BORG_PASSPHRASE

    • Used to decrypt the repository key
    • Never stored in the repository (you must save it separately)
What You Need for Recovery

To restore backups from a new machine, you need:

ComponentWhere it's storedCritical?
Repository accessRemote backup server✅ Yes
PassphraseYou must save this externally✅ Yes
Repository ke

Tag summary

Content type

Image

Digest

sha256:5cfd22cce

Size

25.7 MB

Last updated

3 months ago

docker pull diarmuidk/docker-borg-client