Backup intermediate ⏱ 45 minutes

BorgBackup: Deduplicating Backup with Borgmatic

Space-efficient encrypted backups with Borg. Automate with Borgmatic for hands-off protection.

By homelabbing.io • 2025-12-17
#borgbackup#borgmatic#backup#deduplication#encryption

BorgBackup (Borg) is a deduplicating backup program with compression and encryption. Combined with Borgmatic, it provides an enterprise-grade backup solution perfect for homelabs. This guide covers everything from installation to advanced automation.

Why BorgBackup?

BorgBackup offers exceptional features for homelab environments:

  • Extreme deduplication: Content-defined chunking saves massive amounts of space
  • Compression: Multiple algorithms (lz4, zstd, zlib) reduce storage requirements
  • Encryption: AES-256 encryption with different modes for various security needs
  • Space efficiency: Only stores unique data blocks across all backups
  • Fast backups: Only transfers changed data, backed by efficient chunking
  • Reliable: Atomic commits ensure repository integrity
  • Mountable: Browse and restore files by mounting backups as filesystems
  • Remote support: SSH-based remote repositories with excellent performance

BorgBackup vs Other Solutions

Borg vs Restic:

  • Borg: Better deduplication, faster, more mature
  • Restic: More backends, easier to use, written in Go

Borg vs Duplicity:

  • Borg: Much faster, better deduplication, modern architecture
  • Duplicity: Older, simpler, uses standard formats

Borg vs rsync:

  • Borg: Deduplication, encryption, compression, versioning
  • rsync: Simple mirroring, no built-in versioning or encryption

Part 1: Installing BorgBackup

Installation on Debian/Ubuntu

Install from distribution packages:

sudo apt update
sudo apt install borgbackup -y

For the latest version, use the official repository:

# Add BorgBackup repository
sudo add-apt-repository ppa:costamagnagianfranco/borgbackup
sudo apt update
sudo apt install borgbackup -y

Verify installation:

borg --version

Installation on Other Systems

Fedora/CentOS/RHEL:

sudo dnf install borgbackup -y

From Binary (Latest Version):

# Download latest release
wget https://github.com/borgbackup/borg/releases/download/1.2.7/borg-linux64
sudo mv borg-linux64 /usr/local/bin/borg
sudo chmod +x /usr/local/bin/borg

Install with pip (for latest features):

sudo apt install python3-pip python3-dev libacl1-dev libssl-dev -y
sudo pip3 install borgbackup

Part 2: Setting Up a Borg Repository

Local Repository

Create a local backup repository:

# Create directory for repository
sudo mkdir -p /mnt/backup/borg-repo

# Initialize repository with encryption
borg init --encryption=repokey /mnt/backup/borg-repo

You’ll be prompted to enter a passphrase. This encrypts your backups.

Remote Repository (SSH)

Set up SSH keys for passwordless access:

# Generate SSH key if you don't have one
ssh-keygen -t ed25519 -C "backup-key"

# Copy to backup server
ssh-copy-id backup-user@backup-server

Initialize remote repository:

borg init --encryption=repokey backup-user@backup-server:/path/to/repo

Encryption Modes

BorgBackup offers several encryption modes:

repokey (Recommended):

  • Key stored in repository
  • Passphrase protects the key
  • Easy to use, good security
borg init --encryption=repokey /path/to/repo

keyfile:

  • Key stored in ~/.config/borg/keys/
  • More secure, requires key backup
  • Good for critical data
borg init --encryption=keyfile /path/to/repo

authenticated:

  • No encryption, only authentication
  • Fast, for already encrypted data
  • Use with encrypted storage
borg init --encryption=authenticated /path/to/repo

none:

  • No encryption or authentication
  • Fastest, for public/non-sensitive data
borg init --encryption=none /path/to/repo

Export and Backup Repository Key

CRITICAL: Export your repository key!

# Export key
borg key export /path/to/repo backup-key.txt

# Store securely (offline, password manager, etc.)
# Without this key, you cannot recover your backups!

Part 3: Creating Backups

Basic Backup Command

Create your first backup:

borg create /mnt/backup/borg-repo::backup-2025-12-17     /home     /etc     /var/lib/docker/volumes

The ::backup-2025-12-17 part is the archive name. Each backup is an archive.

Backup with Progress and Statistics

borg create --progress --stats     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home /etc /var/lib/docker/volumes

Compression Options

BorgBackup supports multiple compression algorithms:

lz4 (Fast, recommended for most cases):

borg create --compression lz4     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home

zstd (Excellent balance of speed and ratio):

borg create --compression zstd,3     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home

zlib (Better compression, slower):

borg create --compression zlib,6     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home

auto (Automatic selection):

borg create --compression auto,lz4     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home

Exclusion Patterns

Exclude files and directories:

borg create     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home     --exclude '*/node_modules/*'     --exclude '*.tmp'     --exclude '*/.cache/*'     --exclude '*/Downloads/*'

Use an exclude file:

# Create /root/.borg-exclude
cat > /root/.borg-exclude << 'EOF'
*.log
*.tmp
*~
.DS_Store
.cache/
node_modules/
__pycache__/
*.pyc
.git/
*.iso
*.img
thumbs.db
Thumbs.db
EOF

# Use in backup
borg create     /mnt/backup/borg-repo::backup-$(date +%Y-%m-%d)     /home     --exclude-from /root/.borg-exclude

Archive Naming Conventions

Use consistent, informative archive names:

# Date-based
borg create ::backup-$(date +%Y-%m-%d)

# Date and time
borg create ::backup-$(date +%Y-%m-%d-%H%M)

# Hostname and date
borg create ::$(hostname)-$(date +%Y-%m-%d)

# With tag
borg create ::daily-$(date +%Y-%m-%d)

Part 4: Managing Archives

List Archives

View all archives in repository:

borg list /mnt/backup/borg-repo

List files in specific archive:

borg list /mnt/backup/borg-repo::backup-2025-12-17

List with file details:

borg list --short /mnt/backup/borg-repo::backup-2025-12-17

Archive Information

Get archive statistics:

borg info /mnt/backup/borg-repo::backup-2025-12-17

Repository statistics:

borg info /mnt/backup/borg-repo

Comparing Archives

Show differences between archives:

borg diff /mnt/backup/borg-repo::backup-2025-12-16 backup-2025-12-17

Part 5: Restoring Data

Extract Entire Archive

Restore complete archive:

# Extract to current directory
borg extract /mnt/backup/borg-repo::backup-2025-12-17

# Extract to specific location
mkdir /tmp/restore
cd /tmp/restore
borg extract /mnt/backup/borg-repo::backup-2025-12-17

Extract Specific Files

Restore specific paths:

borg extract /mnt/backup/borg-repo::backup-2025-12-17     home/user/important.txt     etc/nginx/nginx.conf

Use patterns:

borg extract /mnt/backup/borg-repo::backup-2025-12-17     --pattern '+home/user/*.conf'     --pattern '-home/user/.cache'

Mount Archive for Browsing

Mount repository as filesystem:

# Create mount point
mkdir /mnt/borg-mount

# Mount repository
borg mount /mnt/backup/borg-repo /mnt/borg-mount

# Browse archives
ls /mnt/borg-mount/
ls /mnt/borg-mount/backup-2025-12-17/home/user/

# Copy files
cp /mnt/borg-mount/backup-2025-12-17/home/user/important.txt /home/user/

# Unmount when done
borg umount /mnt/borg-mount

Mount specific archive:

borg mount /mnt/backup/borg-repo::backup-2025-12-17 /mnt/borg-mount

Part 6: Pruning and Retention

Prune Old Archives

BorgBackup’s prune command removes old archives based on retention rules:

borg prune     --keep-daily 7     --keep-weekly 4     --keep-monthly 12     --keep-yearly 3     /mnt/backup/borg-repo

More retention options:

borg prune     --keep-last 5     --keep-hourly 24     --keep-daily 7     --keep-weekly 4     --keep-monthly 12     --keep-yearly 3     /mnt/backup/borg-repo

Dry run to see what would be deleted:

borg prune --dry-run --list     --keep-daily 7     --keep-weekly 4     /mnt/backup/borg-repo

Compact Repository

After pruning, compact to reclaim space:

borg compact /mnt/backup/borg-repo

Combined prune and compact:

borg prune --keep-daily 7 --keep-weekly 4 --keep-monthly 12     /mnt/backup/borg-repo
borg compact /mnt/backup/borg-repo

Part 7: Repository Maintenance

Check Repository Integrity

Basic integrity check:

borg check /mnt/backup/borg-repo

Verify all data:

borg check --verify-data /mnt/backup/borg-repo

Check specific archives:

borg check --verify-data /mnt/backup/borg-repo::backup-2025-12-17

Repair Corrupted Repository

If corruption is detected:

borg check --repair /mnt/backup/borg-repo

Part 8: Installing Borgmatic

Borgmatic provides declarative configuration and automation for BorgBackup.

Install Borgmatic

Via pip (recommended):

sudo apt install python3-pip -y
sudo pip3 install --upgrade borgmatic

Via distribution packages:

sudo apt install borgmatic -y

Verify installation:

borgmatic --version

Generate Configuration

Create initial configuration:

sudo mkdir -p /etc/borgmatic
sudo generate-borgmatic-config

This creates /etc/borgmatic/config.yaml.

Part 9: Configuring Borgmatic

Basic Configuration

Edit /etc/borgmatic/config.yaml:

# Location of Borg repositories
repositories:
    - /mnt/backup/borg-repo

# Paths to backup
source_directories:
    - /home
    - /etc
    - /var/lib/docker/volumes
    - /opt

# Paths to exclude
exclude_patterns:
    - '*.tmp'
    - '*.log'
    - '*/.cache/*'
    - '*/node_modules/*'
    - '*/__pycache__/*'
    - '*.pyc'
    - '.git/'

# Repository storage options
storage:
    compression: auto,lz4
    archive_name_format: '{hostname}-{now:%Y-%m-%d-%H%M%S}'

# Retention policy
retention:
    keep_hourly: 24
    keep_daily: 7
    keep_weekly: 4
    keep_monthly: 12
    keep_yearly: 3

# Consistency checks
consistency:
    checks:
        - name: repository
          frequency: 2 weeks
        - name: archives
          frequency: 1 month

# Hooks for notifications
hooks:
    before_backup:
        - echo "Starting backup at $(date)"
    after_backup:
        - echo "Backup completed at $(date)"
    on_error:
        - echo "Backup failed at $(date)"

Advanced Configuration

Enhanced configuration with multiple repositories and features:

repositories:
    - path: /mnt/backup/borg-repo
      label: local
    - path: ssh://backup-user@backup-server/~/borg-repo
      label: offsite

source_directories:
    - /home
    - /etc
    - /var/lib/docker/volumes
    - /opt
    - /root

exclude_patterns:
    - '*.tmp'
    - '*.log'
    - '*/.cache/*'
    - '*/node_modules/*'
    - '*/__pycache__/*'
    - '*.pyc'
    - '.git/'
    - '/home/*/.local/share/Trash'
    - '/home/*/Downloads'

exclude_if_present:
    - .nobackup

storage:
    compression: auto,zstd,3
    archive_name_format: '{hostname}-{now:%Y-%m-%d-%H%M%S}'
    checkpoint_interval: 1800
    encryption_passphrase: "your-encryption-password"
    ssh_command: ssh -i /root/.ssh/backup_key

retention:
    keep_within: 3H
    keep_hourly: 24
    keep_daily: 7
    keep_weekly: 4
    keep_monthly: 12
    keep_yearly: 3
    prefix: '{hostname}-'

consistency:
    checks:
        - name: repository
          frequency: 2 weeks
        - name: archives
          frequency: 1 month
        - name: data
          frequency: 6 months

hooks:
    before_backup:
        - echo "Starting backup at $(date)" >> /var/log/borgmatic.log
        - curl -fsS -m 10 --retry 5 https://hc-ping.com/YOUR-UUID/start > /dev/null

    after_backup:
        - echo "Backup completed at $(date)" >> /var/log/borgmatic.log
        - curl -fsS -m 10 --retry 5 https://hc-ping.com/YOUR-UUID > /dev/null

    on_error:
        - echo "Backup FAILED at $(date)" >> /var/log/borgmatic.log
        - curl -fsS -m 10 --retry 5 https://hc-ping.com/YOUR-UUID/fail > /dev/null
        - 'echo "Backup failed on $(hostname)" | mail -s "Backup Failed" [email protected]'

    postgresql_databases:
        - name: all

    mysql_databases:
        - name: all

Environment Variables for Credentials

Store sensitive data securely:

# Create environment file
sudo nano /etc/borgmatic/environment

# Add variables
export BORG_PASSPHRASE="your-encryption-password"
export BORG_RSH="ssh -i /root/.ssh/backup_key"

Update systemd service to load environment:

sudo systemctl edit borgmatic.timer

Add:

[Service]
EnvironmentFile=/etc/borgmatic/environment

Part 10: Running Borgmatic

Manual Execution

Run backup manually:

sudo borgmatic

With verbose output:

sudo borgmatic --verbosity 1

List archives:

sudo borgmatic list

Show repository info:

sudo borgmatic info

Validate Configuration

Check configuration file:

sudo borgmatic config validate

Extract with Borgmatic

Extract entire archive:

sudo borgmatic extract --archive latest

Extract to specific directory:

sudo borgmatic extract --archive latest --destination /tmp/restore

Extract specific paths:

sudo borgmatic extract --archive latest --path home/user/important.txt

Part 11: Automation with Systemd

Borgmatic includes systemd timer by default.

Enable Systemd Timer

sudo systemctl enable --now borgmatic.timer
sudo systemctl list-timers borgmatic

View timer status:

sudo systemctl status borgmatic.timer

Customize Timer Schedule

Edit timer:

sudo systemctl edit borgmatic.timer

Change schedule:

[Timer]
OnCalendar=
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=30min
Persistent=true

Reload systemd:

sudo systemctl daemon-reload
sudo systemctl restart borgmatic.timer

Monitor Execution

View logs:

sudo journalctl -u borgmatic.service -f

Check last run:

sudo systemctl status borgmatic.service

Part 12: Database Backups

PostgreSQL Integration

Borgmatic can automatically backup PostgreSQL databases:

hooks:
    postgresql_databases:
        - name: myapp
          hostname: localhost
          port: 5432
          username: postgres
          password: your-password

        - name: all
          hostname: localhost

Create .pgpass for authentication:

echo "localhost:5432:*:postgres:your-password" > /root/.pgpass
chmod 600 /root/.pgpass

MySQL/MariaDB Integration

hooks:
    mysql_databases:
        - name: wordpress
          hostname: localhost
          port: 3306
          username: root
          password: your-password

        - name: all
          hostname: localhost
          username: root
          options: --single-transaction

Create .my.cnf for credentials:

sudo nano /root/.my.cnf
[client]
user=root
password=your-password
sudo chmod 600 /root/.my.cnf

Docker Volume Backups

Stop containers before backup:

hooks:
    before_backup:
        - docker stop nginx
        - docker stop database

    after_backup:
        - docker start database
        - docker start nginx

Part 13: Monitoring and Notifications

Healthchecks.io Integration

Add to borgmatic configuration:

hooks:
    before_backup:
        - curl -fsS -m 10 --retry 5 https://hc-ping.com/YOUR-UUID/start > /dev/null

    after_backup:
        - curl -fsS -m 10 --retry 5 https://hc-ping.com/YOUR-UUID > /dev/null

    on_error:
        - curl -fsS -m 10 --retry 5 https://hc-ping.com/YOUR-UUID/fail > /dev/null

Email Notifications

Install mail utilities:

sudo apt install mailutils -y

Configure in borgmatic:

hooks:
    on_error:
        - 'echo "Backup failed on $(hostname) at $(date)" | mail -s "❌ Backup Failed: $(hostname)" [email protected]'

    after_backup:
        - 'echo "Backup completed successfully on $(hostname) at $(date)" | mail -s "✅ Backup Success: $(hostname)" [email protected]'

Ntfy.sh Push Notifications

hooks:
    after_backup:
        - 'curl -d "Backup completed on $(hostname)" ntfy.sh/your-topic'

    on_error:
        - 'curl -H "Priority: high" -d "Backup FAILED on $(hostname)" ntfy.sh/your-topic'

Custom Monitoring Script

Create /usr/local/bin/borgmatic-monitor.sh:

#!/bin/bash

HOSTNAME=$(hostname)
LOGFILE="/var/log/borgmatic-monitor.log"
HEALTHCHECK_URL="https://hc-ping.com/YOUR-UUID"
EMAIL="[email protected]"

# Start signal
curl -fsS -m 10 --retry 5 "$HEALTHCHECK_URL/start" > /dev/null

# Run borgmatic
if borgmatic --verbosity 1 >> "$LOGFILE" 2>&1; then
    echo "$(date): Backup completed successfully" >> "$LOGFILE"
    curl -fsS -m 10 --retry 5 "$HEALTHCHECK_URL" > /dev/null
    echo "Backup completed on $HOSTNAME at $(date)" |         mail -s "✅ Backup Success: $HOSTNAME" "$EMAIL"
else
    echo "$(date): Backup FAILED" >> "$LOGFILE"
    curl -fsS -m 10 --retry 5 "$HEALTHCHECK_URL/fail" > /dev/null
    echo "Backup FAILED on $HOSTNAME at $(date). Check $LOGFILE" |         mail -s "❌ Backup Failed: $HOSTNAME" "$EMAIL"
fi

Part 14: Remote Repository Setup

SSH Server Configuration

On backup server, install BorgBackup:

sudo apt install borgbackup -y

Create backup user:

sudo useradd -m -s /bin/bash borgbackup
sudo mkdir -p /home/borgbackup/.ssh

Add client SSH key:

# On client, copy public key
cat ~/.ssh/id_ed25519.pub

# On server, add to authorized_keys
sudo nano /home/borgbackup/.ssh/authorized_keys
# Paste public key

sudo chown -R borgbackup:borgbackup /home/borgbackup/.ssh
sudo chmod 700 /home/borgbackup/.ssh
sudo chmod 600 /home/borgbackup/.ssh/authorized_keys

Create repository directory:

sudo mkdir -p /backup/repos
sudo chown borgbackup:borgbackup /backup/repos

Restrict SSH Command

For security, restrict SSH access to Borg only:

Edit /home/borgbackup/.ssh/authorized_keys:

command="borg serve --restrict-to-path /backup/repos",restrict ssh-ed25519 AAAA... user@client

Initialize Remote Repository

From client:

borg init --encryption=repokey borgbackup@backup-server:/backup/repos/client-hostname

Part 15: Advanced Features

Append-Only Mode

Protect against ransomware by enabling append-only:

On server:

borg init --encryption=repokey --append-only /backup/repos/client-hostname

Or configure in SSH authorized_keys:

command="borg serve --restrict-to-path /backup/repos --append-only"

Quota Management

Limit repository size:

On server, configure quota in SSH command:

command="borg serve --restrict-to-path /backup/repos --storage-quota 500G"

Using Borg with Multiple Machines

Create separate repository for each machine:

# Machine 1
borg init --encryption=repokey /mnt/backup/borg-repo/server1

# Machine 2
borg init --encryption=repokey /mnt/backup/borg-repo/server2

Or use single repository with hostname prefixes:

# borgmatic config
storage:
    archive_name_format: '{hostname}-{now:%Y-%m-%d-%H%M%S}'

Part 16: Security Best Practices

Encryption

  1. Use repokey or keyfile: Always encrypt your backups
  2. Strong passphrases: Use 20+ character random passphrases
  3. Export and backup keys: Store repository keys offline
  4. Separate credentials: Different passphrases per repository

Access Control

  1. SSH key-based auth: Never use password authentication
  2. Restrict SSH commands: Use borg serve with restrictions
  3. Append-only mode: Prevent deletion by compromised clients
  4. User separation: Dedicated backup user with minimal permissions

Network Security

  1. SSH only: Never expose Borg over HTTP
  2. Firewall rules: Restrict SSH access to backup clients
  3. VPN/Tailscale: Additional network isolation
  4. Key management: Rotate SSH keys periodically

Verification

Create verification script /usr/local/bin/borg-verify.sh:

#!/bin/bash

REPO="/mnt/backup/borg-repo"
export BORG_PASSPHRASE="your-passphrase"

# Check repository
echo "Checking repository integrity..."
borg check "$REPO"

# Verify random archive
ARCHIVE=$(borg list "$REPO" --short | shuf -n 1)
echo "Verifying archive: $ARCHIVE"
borg check --verify-data "$REPO::$ARCHIVE"

# Test extract
echo "Testing extraction..."
mkdir -p /tmp/borg-test
borg extract "$REPO::$ARCHIVE" etc/hostname --destination /tmp/borg-test

if [ -f /tmp/borg-test/etc/hostname ]; then
    echo "✅ Verification successful"
    rm -rf /tmp/borg-test
    exit 0
else
    echo "❌ Verification failed"
    exit 1
fi

Part 17: Performance Tuning

Optimize Compression

Test different compression algorithms:

# Fast backup (lz4)
borg create --compression lz4 ::test-lz4 /home

# Balanced (zstd)
borg create --compression zstd,3 ::test-zstd /home

# Maximum compression (zlib)
borg create --compression zlib,9 ::test-zlib /home

# Compare sizes
borg info ::test-lz4
borg info ::test-zstd
borg info ::test-zlib

Checkpoint Intervals

For large backups, use checkpoints:

borg create --checkpoint-interval 1800 ::backup-large /home

Or in borgmatic:

storage:
    checkpoint_interval: 1800

Upload Buffer

Increase upload buffer for better performance:

export BORG_FILES_CACHE_TTL=20
export BORG_UPLOAD_BUFFER=20M

Part 18: Troubleshooting

Common Issues

Lock timeout:

# List locks
borg list locks /mnt/backup/borg-repo

# Break lock (if safe)
borg break-lock /mnt/backup/borg-repo

Repository integrity errors:

# Check repository
borg check /mnt/backup/borg-repo

# Repair if needed
borg check --repair /mnt/backup/borg-repo

Out of space:

# Check space usage
borg info /mnt/backup/borg-repo

# Aggressive pruning
borg prune --keep-last 3 /mnt/backup/borg-repo
borg compact /mnt/backup/borg-repo

Slow backups:

# Check what's being backed up
borg create --dry-run --list ::test /home

# Exclude large files
borg create --exclude '*.iso' --exclude '*.img' ::backup /home

Debug Mode

Enable debug output:

borg create --debug --show-rc ::debug-backup /home

Check borgmatic in debug mode:

borgmatic --verbosity 2

Conclusion

You now have a complete BorgBackup solution with Borgmatic automation. This setup provides:

  • Highly efficient deduplicated backups
  • Strong encryption and security
  • Automated scheduled backups
  • Database integration
  • Monitoring and notifications
  • Remote and local repository support
  • Easy restore procedures

Next Steps

  1. Test your backups: Regular restore tests are critical
  2. Monitor backup status: Set up health checks and alerts
  3. Document recovery procedures: Write down exact restore steps
  4. Export repository keys: Store them securely offline
  5. Plan for growth: Monitor repository size and adjust retention

Additional Resources

Remember: Backups without tested restores are just expensive hopes. Test your restore procedures regularly!