BorgBackup Advanced Strategies

BorgBackup is a deduplicating, compressing, encrypting backup tool widely used in enterprise Linux environments. This guide covers advanced BorgBackup strategies including append-only mode for ransomware protection, remote repositories over SSH, compression tuning, parallel operations, monitoring, and disaster recovery procedures.

Prerequisites

  • Ubuntu/Debian or CentOS/Rocky Linux
  • BorgBackup 1.2+
  • SSH access to a remote backup server (for remote repos)
  • At least 1 GB RAM for compression operations on large datasets

Installation and Repository Setup

# Ubuntu/Debian
sudo apt-get install -y borgbackup

# CentOS/Rocky
sudo dnf install -y borgbackup

# Or install latest binary
pip3 install borgbackup

# Verify installation
borg --version

# Create passphrase file (protect with strict permissions)
echo "your-strong-borg-passphrase" > /root/.borg-passphrase
chmod 600 /root/.borg-passphrase

# Export passphrase for borg commands
export BORG_PASSPHRASE="$(cat /root/.borg-passphrase)"

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

# Export and SAVE the repository key (critical for recovery!)
borg key export /backup/borg-repo /root/.borg-repokey-backup
# Store this key offline - you cannot recover data without it

Append-Only Mode for Ransomware Protection

Append-only mode prevents deletion or modification of existing archives — a compromised client cannot destroy backups:

# Initialize a repository in append-only mode
borg init --encryption=repokey-blake2 --append-only /backup/append-only-repo

# Create backups normally - archives can only be added
borg create /backup/append-only-repo::{hostname}-{now:%Y-%m-%d} /etc /home

# Pruning must be done from a trusted admin machine
# The admin uses a separate key with full access
# On the SERVER with full access:
borg compact /backup/append-only-repo

# For remote append-only repos, use restricted SSH key
# The backup client's SSH key is restricted with borg serve --append-only

Restricted SSH Key for Remote Append-Only

On the backup SERVER, add to ~/.ssh/authorized_keys:

# Prepend to the SSH key line to restrict it to append-only borg
command="borg serve --append-only --restrict-to-path /srv/borg/client01",restrict ssh-ed25519 AAAA... backupclient@client01

Full access key for admin operations (pruning, compacting):

command="borg serve --restrict-to-path /srv/borg/client01",restrict ssh-ed25519 AAAA... admin@management

Remote Repositories over SSH

# SSH repository format: user@host:path
# Initialize remote repository
borg init \
  --encryption=repokey-blake2 \
  [email protected]:/srv/borg/server01

# Specify SSH key explicitly
BORG_RSH="ssh -i /root/.ssh/borg_backup_key" \
borg init \
  --encryption=repokey-blake2 \
  [email protected]:/srv/borg/server01

# Set persistent SSH options via env
export BORG_RSH="ssh -i /root/.ssh/borg_key -o StrictHostKeyChecking=no -o ConnectTimeout=10"

# Create remote backup
borg create \
  [email protected]:/srv/borg/server01::{hostname}-{now:%Y-%m-%dT%H:%M:%S} \
  /etc /home /var/www \
  --exclude /home/*/.cache \
  --stats \
  --progress

Complete Backup Script for Remote Repository

cat > /usr/local/bin/borg-backup.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

# Configuration
export BORG_REPO="[email protected]:/srv/borg/$(hostname)"
export BORG_PASSPHRASE="$(cat /root/.borg-passphrase)"
export BORG_RSH="ssh -i /root/.ssh/borg_key -o ServerAliveInterval=60"

BACKUP_DIRS="/etc /home /var/www /opt"
ARCHIVE_NAME="{hostname}-{now:%Y-%m-%dT%H:%M:%S}"

# Trap errors to send alert
trap 'echo "Borg backup FAILED on $(hostname) at $(date)" | \
  mail -s "BACKUP FAILED: $(hostname)" [email protected]' ERR

echo "=== Borg backup started: $(date) ==="

borg create \
  --verbose \
  --filter AME \
  --list \
  --stats \
  --show-rc \
  --compression lz4 \
  --exclude-caches \
  --exclude '/home/*/.cache' \
  --exclude '/var/cache' \
  --exclude '/tmp' \
  --exclude '*.pyc' \
  "$BORG_REPO::$ARCHIVE_NAME" \
  $BACKUP_DIRS

echo "=== Pruning old archives ==="
borg prune \
  --list \
  --glob-archives "{hostname}-*" \
  --show-rc \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  "$BORG_REPO"

echo "=== Compacting repository ==="
borg compact "$BORG_REPO"

echo "=== Backup complete: $(date) ==="
SCRIPT

chmod +x /usr/local/bin/borg-backup.sh

Compression Tuning

Borg supports several compression algorithms with different speed/ratio tradeoffs:

# lz4: Fast compression, lower ratio (best for daily backups)
borg create --compression lz4 /backup/repo::backup-lz4 /data

# zstd: Good balance of speed and ratio (recommended)
borg create --compression zstd /backup/repo::backup-zstd /data

# zstd with specific level (1-22, higher = smaller but slower)
borg create --compression zstd,3 /backup/repo::backup-zstd3 /data  # Fast
borg create --compression zstd,15 /backup/repo::backup-zstd15 /data # Slow, small

# lzma: Maximum compression, slowest (use for archival/cold storage)
borg create --compression lzma,6 /backup/repo::archive /data

# auto: Let Borg decide based on file type
borg create --compression auto,lz4 /backup/repo::backup-auto /data

# Benchmark compression on your actual data
borg benchmark crud /tmp/borg-bench /data

Pruning and Retention Policies

# Prune with a detailed retention policy
borg prune \
  --list \
  --show-rc \
  --keep-within 2d \          # Keep all archives from last 2 days
  --keep-hourly 24 \          # Keep 24 hourly archives
  --keep-daily 7 \            # Keep 7 daily archives
  --keep-weekly 4 \           # Keep 4 weekly archives
  --keep-monthly 12 \         # Keep 12 monthly archives
  --keep-yearly 3 \           # Keep 3 yearly archives
  --glob-archives "{hostname}-*" \  # Only prune matching archives
  /backup/borg-repo

# Dry run to preview what would be deleted
borg prune --dry-run --list --keep-daily 7 /backup/borg-repo

# Compact after prune (frees actual disk space)
borg compact /backup/borg-repo

Monitoring and Alerting

Systemd Integration

# /etc/systemd/system/borg-backup.service
[Unit]
Description=BorgBackup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-backup.sh
StandardOutput=journal
StandardError=journal
Environment="BORG_PASSPHRASE="
EnvironmentFile=/etc/borg/environment

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/borg-backup.timer
[Unit]
Description=BorgBackup daily timer

[Timer]
OnCalendar=02:00
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target
# Store credentials securely
sudo mkdir -p /etc/borg
sudo tee /etc/borg/environment << 'EOF'
BORG_PASSPHRASE=your-passphrase
BORG_RSH=ssh -i /root/.ssh/borg_key
EOF
sudo chmod 600 /etc/borg/environment

sudo systemctl daemon-reload
sudo systemctl enable --now borg-backup.timer

Healthchecks Integration

# Send ping to healthchecks.io on success/failure
HEALTHCHECK_URL="https://hc-ping.com/your-uuid"

# At start of backup script:
curl -m 10 --retry 3 "$HEALTHCHECK_URL/start"

# At end of backup script (success):
curl -m 10 --retry 3 "$HEALTHCHECK_URL"

# On failure (in trap):
curl -m 10 --retry 3 "$HEALTHCHECK_URL/fail" -d "Backup failed: $?"

Parallel Operations

# Run backups for multiple clients in parallel from an orchestration server
backup_client() {
    local host=$1
    local repo="/srv/borg/$host"

    echo "Starting backup for $host"
    ssh -i /root/.ssh/borg_key "backupuser@$host" \
      "BORG_PASSPHRASE=secret borg create $repo::{hostname}-{now} /etc /home" \
      && echo "Backup complete for $host" \
      || echo "BACKUP FAILED for $host"
}

export -f backup_client
parallel -j 4 backup_client ::: server1 server2 server3 server4

# Or use xargs for parallel execution
echo "server1 server2 server3 server4" | tr ' ' '\n' | \
  xargs -P 4 -I {} bash -c 'backup_client {}'

Disaster Recovery

# List all archives in a repository
borg list /backup/borg-repo

# List contents of a specific archive
borg list /backup/borg-repo::server01-2024-01-15T02:00:00

# Extract specific files from an archive
mkdir -p /restore
cd /restore
borg extract /backup/borg-repo::server01-2024-01-15T02:00:00 \
  home/user/important-files

# Extract entire archive
borg extract /backup/borg-repo::server01-2024-01-15T02:00:00

# Extract with dry run (see what would be extracted)
borg extract --dry-run --list /backup/borg-repo::server01-2024-01-15T02:00:00

# Mount an archive as a filesystem (requires FUSE)
sudo apt-get install -y borgbackup-fuse
mkdir -p /mnt/borg-restore
borg mount /backup/borg-repo::server01-2024-01-15T02:00:00 /mnt/borg-restore
ls /mnt/borg-restore
# Copy specific files
cp -r /mnt/borg-restore/var/www/myapp /restore/
# Unmount
borg umount /mnt/borg-restore

Troubleshooting

Repository lock left behind:

# Only break lock if no backup is running
borg break-lock /backup/borg-repo

# Check for running borg processes first
ps aux | grep borg

SSH connection timeout during backup:

# Add keepalive to BORG_RSH
export BORG_RSH="ssh -i /root/.ssh/borg_key -o ServerAliveInterval=60 -o ServerAliveCountMax=10"

"Repository is corrupted" errors:

# Check repository integrity
borg check --repository-only /backup/borg-repo

# Check archives too (slower)
borg check /backup/borg-repo

# Repair if possible
borg check --repair /backup/borg-repo

High memory usage during backup:

# Borg uses chunker cache - limit with env var
export BORG_FILES_CACHE_TTL=20
# Or reduce chunk cache size
export BORG_CACHE_DIR=/tmp/borg-cache

Conclusion

BorgBackup's append-only mode combined with remote repositories provides enterprise-grade backup security against both accidental deletion and ransomware. Its deduplication ensures even frequent backups remain storage-efficient, while flexible compression tuning lets you balance speed against storage costs based on your data types. With systemd timer integration and monitoring pings, you can operate a reliable, automated backup infrastructure with minimal ongoing maintenance.