Why BorgBackup?
The 3-2-1 backup rule (three copies, two different media types, one off-site) is well-known. The harder problem is implementing it reliably without paying a fortune or spending hours on configuration. BorgBackup solves the technical side elegantly:
- Deduplication: Borg breaks files into variable-length chunks and only stores chunks that haven't been seen before. A 50 GB database that changes 100 MB daily uses roughly 100 MB of new storage per backup, not 50 GB.
- Encryption: Repository contents are encrypted with AES-256-CTR before being written. Even if your backup destination is compromised, the data is unreadable without your passphrase.
- Compression: Borg supports lz4 (fast), zstd (balanced), and zlib/lzma (maximum compression).
- Integrity checking: Every chunk is stored with a checksum.
borg checkverifies the entire repository against these checksums.
Installation
# Debian / Ubuntu
apt install borgbackup
# Check version
borg --version
# Install borgmatic (backup automation wrapper)
pip3 install borgmatic
# or via apt on newer systems:
apt install borgmatic
For off-site backups, you'll need a remote destination: a second server, a rented storage server (Hetzner Storage Box works excellently with Borg), or any host where you can SSH. Install BorgBackup on the remote as well.
Initializing a Repository
A Borg repository is a directory (local or remote over SSH) that stores all your backup archives. Create one with borg init:
# Local repository (for testing)
borg init --encryption=repokey /mnt/backup/myserver
# Remote repository over SSH (recommended for off-site)
borg init --encryption=repokey-blake2 [email protected]:repos/myserver
You'll be prompted for a passphrase. Store it securely - in a password manager, on paper in a safe, or in a secrets manager. Without the passphrase, encrypted backups are unrecoverable. The repokey-blake2 encryption mode stores the repository key in the repository itself (encrypted by your passphrase), which simplifies key management.
# Export and save your repository key separately
borg key export [email protected]:repos/myserver /secure/location/borg-key-backup
Your First Backup
# Create an archive named with the current date and time
borg create \
--compression zstd,3 \
--stats \
[email protected]:repos/myserver::'{hostname}-{now:%Y-%m-%dT%H:%M}' \
/etc \
/home \
/var/www \
/var/lib/postgresql \
--exclude /var/www/*/node_modules \
--exclude /home/*/.cache
The archive name uses Borg's format strings: {hostname} expands to your server's hostname and {now:%Y-%m-%dT%H:%M} to the current timestamp. The --stats flag prints deduplicated size information after the backup.
# List all archives in a repository
borg list [email protected]:repos/myserver
# List files in a specific archive
borg list [email protected]:repos/myserver::myserver-2025-04-28T03:00
Automated Backups with borgmatic
Running Borg manually is fine for testing, but production backups need automation and monitoring. borgmatic is a wrapper that handles scheduling, retention, consistency checks, and email notifications. Configure it in /etc/borgmatic/config.yaml:
repositories:
- path: [email protected]:repos/myserver
label: offsite
source_directories:
- /etc
- /home
- /var/www
- /var/lib/postgresql
exclude_patterns:
- "*/node_modules"
- "*/.cache"
- "*/tmp"
compression: zstd,3
retention:
keep_hourly: 24
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
keep_yearly: 2
checks:
- name: repository
frequency: 2 weeks
- name: archives
frequency: 1 month
storage:
encryption_passphrase: "your_secure_passphrase_here"
# Better: use a passcommand to fetch from a secret store
# encryption_passcommand: "cat /root/.borg-passphrase"
hooks:
on_error:
- echo "Backup failed on $(hostname)" | mail -s "Borg Backup FAILED" [email protected]
Run borgmatic manually to test: borgmatic --verbosity 1. Then set up a daily systemd timer or cron job:
# Create a systemd timer for daily backups at 3am
# /etc/systemd/system/borgmatic.timer
[Unit]
Description=Run borgmatic backup daily
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
systemctl enable --now borgmatic.timer
Restoring from Backup
The restore workflow is straightforward, but test it before you need it:
# Mount an archive to browse files
mkdir /mnt/restore
borg mount [email protected]:repos/myserver::myserver-2025-04-28T03:00 /mnt/restore
# Browse and copy files as needed
ls /mnt/restore/etc/
cp /mnt/restore/etc/nginx/nginx.conf /etc/nginx/nginx.conf
# Unmount when done
borg umount /mnt/restore
# Extract specific paths from an archive
borg extract \
[email protected]:repos/myserver::myserver-2025-04-28T03:00 \
etc/nginx \
var/www/myapp