How to Automate Docker & Homelab Backups with Borgmatic and BorgBackup to Cloud S3 Storage

Linux Systemadministrator konfiguriert automatisierte Borgmatic Backups mit S3 Cloud Speicher im Serverraum
How to Automate Docker & Homelab Backups with Borgmatic and BorgBackup to Cloud S3 Storage 3

In containerized homelabs and production Docker environments, traditional backup strategies frequently fall short. Simple file archiving scripts (such as tar -czf) duplicate identical data repeatedly, consume massive disk bandwidth, and often produce corrupted backups when copying live database files directly from volume directories. Furthermore, storing unencrypted archives on local disks violates the fundamental 3-2-1 backup principle, leaving infrastructure vulnerable to hardware failure and ransomware.

BorgBackup (or simply Borg) solves these foundational challenges through authenticated, client-side encryption (AES-256 or ChaCha20-Poly1305) and content-defined chunking deduplication. Only newly created or altered file blocks are stored across backup iterations, routinely shrinking multi-gigabyte daily snapshots into megabytes of physical storage. Borgmatic acts as the declarative orchestration engine for Borg: it reads a unified YAML configuration, coordinates pre-backup database dumps, manages archive creation, prunes aged snapshots, executes data integrity checks, and reports health statuses to monitoring dashboards.

In this technical guide, we will build an automated, containerized backup pipeline using Docker Compose, Borgmatic, and an S3-compatible cloud storage replication backend (such as AWS S3, MinIO, or Backblaze B2 via Rclone).

Architecture: Automated Deduplicated Backup Pipeline

A reliable backup pipeline must execute atomically. Backups should begin with live database dumps, proceed with filesystem volume archiving and deduplication, verify archive integrity, and finally replicate encrypted repository segments offsite to remote object storage.

+-----------------------------------------------------------------------------+
|                               Local Docker Host                             |
|                                                                             |
|  +---------------------------+       +-----------------------------------+  |
|  | Production Workloads      |       | Borgmatic Container (Scheduled)   |  |
|  | - PostgreSQL / MariaDB    | <=== | - Pre-backup Hooks (DB Dumps)     |  |
|  | - Application Volumes     |       | - Deduplicated Encryption Engine  |  |
|  +-------------+-------------+       +-----------------+-----------------+  |
|                |                                       |                    |
|                | Stream Dumps & Read Volumes           |                    |
|                +---------------------------------------+                    |
|                                                        |                    |
|                                                        v                    |
|                              +-------------------------------------------+  |
|                              | Local Borg Repository (/mnt/backups/borg) |  |
|                              | - Authenticated Chunk-Level Deduplication |  |
|                              | - Pruning Policy (Retention Management)   |  |
|                              | - Archive Integrity & Verification Checks  |  |
|                              +---------------------+---------------------+  |
|                                                    |                        |
|                                                    | Post-backup Hook       |
|                                                    | (Rclone Sync)          |
|                                                    v                        |
+----------------------------------------------------+------------------------+
                                                     |
                                                     | Encrypted Blocks (TLS)
                                                     v
                                      +-------------------------------+
                                      | Offsite S3 Cloud Storage      |
                                      | (MinIO, Backblaze B2, AWS S3) |
                                      +-------------------------------+

Key Architecture Advantages

  • Content-Defined Chunking: Files are sliced into dynamic chunks based on byte patterns rather than fixed offsets. Inserting a single byte at the beginning of a multi-gigabyte file does not invalidate subsequent chunk hashes, maximizing deduplication efficiency.
  • Zero-Downtime Database Dumps: Borgmatic streams database dumps (via pg_dump or mysqldump) directly into Borg archives in-flight, eliminating intermediate disk I/O and preventing dirty reads.
  • Cryptographic Client-Side Isolation: Backups are encrypted before leaving the local host. Even if the offsite S3 bucket is compromised, an attacker without the repository passphrase holds only opaque, authenticated ciphertext blocks.

Step 1: Directory Setup & Storage Layout

We will configure persistent host directories for Borgmatic configurations, SSH/Rclone keys, local cache files, and the primary local Borg repository:

sudo mkdir -p /opt/borgmatic/{config,borg-cache,borg-repo,rclone-config}
sudo mkdir -p /var/docker-data # Example application data directory
cd /opt/borgmatic

Create a secure environment file (.env) to hold repository credentials, encryption passphrases, and monitoring webhook tokens:

cat <<EOF > .env
# Borg Repository Encryption Passphrase
BORG_PASSPHRASE=$(openssl rand -base64 32)

# Monitoring Webhook (Healthchecks.io or Uptime Kuma)
HEALTHCHECKS_URL=https://hc-ping.com/your-uuid-here

# Database Credentials
POSTGRES_USER=appuser
POSTGRES_PASSWORD=supersecretpassword
POSTGRES_DB=appdb
EOF

sudo chmod 600 .env

Step 2: Production Docker Compose Definition

Create the docker-compose.yml file. We deploy an active Borgmatic container alongside an example PostgreSQL 16 database to demonstrate automated database dumping and filesystem volume replication:

services:
  borgmatic:
    image: b3vis/borgmatic:latest
    container_name: borgmatic-runner
    restart: unless-stopped
    environment:
      - BORG_PASSPHRASE=${BORG_PASSPHRASE}
      - TZ=Etc/UTC
      - BORG_CACHE_DIR=/root/.cache/borg
    volumes:
      # Borgmatic configuration file
      - /opt/borgmatic/config:/etc/borgmatic.d:ro
      # Local Borg repository
      - /opt/borgmatic/borg-repo:/mnt/repository
      # Borg chunk cache
      - /opt/borgmatic/borg-cache:/root/.cache/borg
      # Host Docker volumes to back up
      - /var/docker-data:/mnt/source/docker-data:ro
      # Rclone config for offsite S3 sync
      - /opt/borgmatic/rclone-config:/root/.config/rclone
      # Read-only access to host docker socket for container inspect/hooks
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - backup-net

  # Example production database to protect
  database:
    image: docker.io/library/postgres:16-alpine
    container_name: production-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pg-data:/var/lib/postgresql/data
    networks:
      - backup-net

volumes:
  pg-data:

networks:
  backup-net:
    driver: bridge

Step 3: Declarative Borgmatic Configuration (config.yaml)

Create the declarative configuration file at /opt/borgmatic/config/config.yaml. This file instructs Borgmatic where to find repositories, which paths to include or exclude, how to execute database dumps, which retention tiers to keep, and how to verify integrity:

# Repository definitions
repositories:
  - path: /mnt/repository
    label: local-nvme-repo

# Source directories to include
source_directories:
  - /mnt/source/docker-data

# Exclude temporary and transient files
exclude_patterns:
  - '*.tmp'
  - '*.log'
  - '*/cache/*'
  - '*/node_modules/*'

# Encryption and compression algorithms
storage:
  encryption: repokey-blake2
  compression: zstd,3
  checkpoint_interval: 1800
  extra_borg_options:
    create: --stats

# Retention policy (Pruning)
retention:
  keep_hourly: 24
  keep_daily: 7
  keep_weekly: 4
  keep_monthly: 6
  keep_yearly: 1
  prefix: '{hostname}-'

# Repository consistency verification
consistency:
  checks:
    - repository
    - archives
  check_last: 3
  prefix: '{hostname}-'

# Database Hooks
hooks:
  postgresql_databases:
    - name: appdb
      hostname: production-postgres
      port: 5432
      username: appuser
      password: supersecretpassword
      format: custom

  # Monitoring and offsite replication hooks
  before_backup:
    - echo "Starting scheduled Borgmatic backup cycle..."
  
  after_backup:
    - echo "Backup and pruning completed. Synchronizing to S3..."
    - rclone sync /mnt/repository s3-remote:homelab-borg-backups/$(hostname) --fast-list --transfers 4
  
  on_error:
    - echo "Borgmatic backup encountered a failure!"
    - curl -fsS -m 10 --retry 3 "${HEALTHCHECKS_URL}/fail" || true

  after_everything:
    - curl -fsS -m 10 --retry 3 "${HEALTHCHECKS_URL}" || true

Configuration Deep-Dive

  • repokey-blake2: Utilizes BLAKE2b for cryptographic MAC hashing paired with authenticated AES-256 payload encryption, offering superior CPU performance over legacy SHA-256.
  • compression: zstd,3: Zstandard level 3 provides high compression ratios with exceptional decompression throughput, outperforming gzip while minimizing CPU load.
  • postgresql_databases hook: Streams binary-compressed pg_dump data directly into the Borg archive without writing an unencrypted intermediate SQL dump to disk.
  • rclone sync: Once the local archive is confirmed and verified, Rclone mirrors the immutable segment files to S3 object storage.

Step 4: Configuring Rclone for S3 Cloud Storage

Create the Rclone configuration file at /opt/borgmatic/rclone-config/rclone.conf to connect to your remote S3-compatible provider (MinIO, Backblaze B2, Cloudflare R2, or AWS S3):

[s3-remote]
type = s3
provider = Other
env_auth = false
access_key_id = YOUR_S3_ACCESS_KEY
secret_access_key = YOUR_S3_SECRET_KEY
endpoint = https://s3.us-east-005.backblazeb2.com
acl = private

Ensure file permissions are restricted to the container root user:

sudo chmod 600 /opt/borgmatic/rclone-config/rclone.conf

Step 5: Initializing the Repository & Executing the First Run

Start the Docker Compose stack:

docker compose up -d

Before Borgmatic can create archives, the target repository must be cryptographically initialized. Execute the init command inside the Borgmatic container:

docker compose exec borgmatic borgmatic init --encryption repokey-blake2

Trigger an immediate manual backup run with verbose output to verify database connectivity, filesystem scanning, and S3 replication:

docker compose exec borgmatic borgmatic create --verbosity 1 --stats

The resulting output will display comprehensive deduplication statistics:

------------------------------------------------------------------------------
Archive name: myhost-2026-10-03T06:20:15
Archive fingerprint: d98f3b9c8e10471b69203a95e7c85a5332f1a6f...
Time (start): Sat, 2026-10-03 06:20:15
Time (end):   Sat, 2026-10-03 06:20:24
Duration: 9.12 seconds
Number of files: 14,820
Original size: 4.85 GB
Compressed size: 1.42 GB
Deduplicated size: 1.41 GB
------------------------------------------------------------------------------

Subsequent runs on largely unchanged data will complete in seconds, with the Deduplicated size metric reflecting only the incremental modified bytes.

Step 6: Disaster Recovery & Granular Restoration

A backup is meaningless unless it can be reliably restored under disaster scenarios.

1. Listing Existing Archives

Inspect all available restore points in the repository:

docker compose exec borgmatic borgmatic list

2. Extracting Files or Entire Directories

To extract specific files from an archive into a recovery staging directory:

mkdir -p /opt/borgmatic/restore
cd /opt/borgmatic/restore

docker compose exec -w /mnt/restore borgmatic \
  borgmatic extract --archive myhost-2026-10-03T06:20:15 \
  --path mnt/source/docker-data/nginx/nginx.conf

3. Restoring the PostgreSQL Database Dump

Extract the PostgreSQL database dump from the archive and pipe it back into the running database container:

docker compose exec -w /mnt/restore borgmatic \
  borgmatic extract --archive myhost-2026-10-03T06:20:15 \
  --path root/.borgmatic/postgresql_databases/appdb

# Restore using pg_restore
docker compose exec -T database pg_restore \
  -U appuser -d appdb --clean --if-exists < /opt/borgmatic/restore/root/.borgmatic/postgresql_databases/appdb

Troubleshooting Common Operational Issues

Issue 1: Repository Lock Contention

Symptom: Borgmatic commands fail immediately with Failed to create/acquire the lock /mnt/repository/lock.exclusive.

Root Cause: An earlier backup job was forcibly terminated (e.g., due to an abrupt host reboot or container restart) leaving a stale lockfile in place.

Resolution: Verify no active backup processes are running, then release the lock:

docker compose exec borgmatic borg break-lock /mnt/repository

Issue 2: Database Connection Refused During Pre-Backup Hook

Symptom: Borgmatic aborts with pg_dump: error: could not connect to server: Connection refused.

Root Cause: The database container is not reachable over the specified Docker bridge network or hostname.

Resolution: Ensure both the borgmatic service and the target database reside within the same user-defined bridge network (e.g., backup-net), and verify that the hostname directive in config.yaml matches the database’s container service name.

Issue 3: Rclone S3 Synchronization Exceeds Bandwidth Limits

Symptom: Offsite cloud synchronization saturates upload bandwidth, causing packet loss and latency spikes across the local network.

Resolution: In config.yaml, add Rclone’s --bwlimit flag to throttle network throughput during offsite transfers:

  after_backup:
    - rclone sync /mnt/repository s3-remote:homelab-borg-backups/$(hostname) --bwlimit 15M --fast-list

Conclusion & Best Practices

Automating your backup strategy with Borgmatic and BorgBackup transforms disaster recovery from an error-prone manual chore into a deterministic, cryptographically secure background process. With content-defined chunking deduplication, full daily snapshots require only a fraction of physical disk space while maintaining granular historical retention.

To ensure total operational resilience, adhere to these key practices: regularly schedule automated disaster recovery drills by restoring files to an isolated environment, keep your repository encryption key exported and stored in an offline password manager, and always connect monitoring pings (such as Healthchecks.io) to receive instant notifications if a backup cycle fails.