TL;DR — Quick Summary
Borgmatic automates encrypted, deduplicated BorgBackup for Linux servers. Configure YAML, schedule via systemd timers, and implement 3-2-1 backup strategy.
Borgmatic turns BorgBackup’s powerful but verbose CLI into a declarative YAML configuration — one config file drives encrypted, deduplicated, compressed backups with retention policies, consistency checks, database hooks, and monitoring notifications. This guide covers every step from installation to production-ready 3-2-1 backup strategy on a Linux server.
Prerequisites
- Linux server (Ubuntu 20.04+, Debian 11+, RHEL 8+, or similar)
- Python 3.8+ (for pip install) or access to apt/dnf/pacman
- SSH access to a remote backup destination (for off-site backups)
- Root or sudo access
- Basic familiarity with YAML and systemd
BorgBackup Fundamentals
BorgBackup is a deduplicating backup program written in Python and C. Before Borgmatic makes sense, understand what Borg gives you:
- Deduplication — Borg splits files into variable-size chunks and stores each unique chunk once. A 100 GB server with mostly unchanged data takes only megabytes for subsequent backups.
- Encryption —
repokey-blake2mode stores an AES-256-CTR encrypted key in the repository, protected by your passphrase. Even if the remote server is compromised, data is unreadable without the passphrase. - Compression —
lz4(fast),zstd(balanced), orlzma(max).zstd,3is the modern default. - Append-only mode — Repositories can be set append-only so that even a compromised client cannot delete existing archives, only add new ones.
Step 1: Install BorgBackup and Borgmatic
Via pip (recommended — always latest version):
pip install --upgrade borgmatic
Via apt (Ubuntu/Debian):
apt install borgmatic
Via Docker:
docker run --rm -v /etc/borgmatic:/etc/borgmatic \
-v /var/backup:/backup \
ghcr.io/borgmatic-collective/borgmatic
Verify installation:
borgmatic --version
borg --version
Step 2: Configure Borgmatic
Generate a commented template:
mkdir -p /etc/borgmatic
borgmatic config generate --destination /etc/borgmatic/config.yaml
A minimal production config for a web server:
source_directories:
- /etc
- /var/www
- /home
- /var/lib/postgresql # only if not using database hooks
repositories:
- path: /var/backup/local-borg
label: local
- path: ssh://backup@192.168.1.100/~/backups/webserver
label: remote-lan
- path: ssh://backup@offsite.example.com:22/~/backups/webserver
label: remote-offsite
storage:
encryption_passphrase: "your-long-random-passphrase-here"
compression: "zstd,3"
archive_name_format: "{hostname}-{now:%Y-%m-%dT%H:%M:%S}"
checkpoint_interval: 1800
retention:
keep_within: 3H
keep_hourly: 24
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
keep_yearly: 1
consistency:
checks:
- name: repository
frequency: always
- name: archives
frequency: 2 weeks
postgresql_databases:
- name: myapp_production
username: postgres
password: dbpassword
format: custom
hooks:
before_backup:
- echo "Starting backup at $(date)"
after_backup:
- curl -fsS -m 10 --retry 5 https://hc-ping.com/your-uuid > /dev/null
on_error:
- echo "Backup FAILED at $(date)" | mail -s "Backup Error" admin@example.com
Key configuration fields:
| Field | Purpose | Recommended value |
|---|---|---|
encryption_passphrase | Protects repository key | 40+ random characters |
compression | Algorithm + level | zstd,3 |
keep_daily | Daily archives retained | 7 |
keep_weekly | Weekly archives retained | 4 |
keep_monthly | Monthly archives retained | 6 |
checkpoint_interval | Seconds between checkpoints | 1800 |
Step 3: Initialize the Repository
Initialize each repository before the first backup:
borgmatic init --encryption repokey-blake2
For an append-only remote repository (highly recommended for off-site):
# On the backup server, edit ~/.ssh/authorized_keys:
command="borg serve --restrict-to-path /home/backup/backups --append-only",restrict ssh-rsa AAAA... client@webserver
# Then initialize normally from client — Borg will create an append-only repo
borgmatic init --encryption repokey-blake2
Export and store your repository key offline:
borg key export /var/backup/local-borg /root/borg-key-backup.txt
# Store this file in a password manager or offline USB — you need it if the repo moves
Step 4: Run Backups
First backup (verbose to verify):
borgmatic create --verbosity 1 --list --stats
Regular operations:
borgmatic # create + prune + compact + check (default)
borgmatic create # backup only
borgmatic prune # apply retention policy, remove old archives
borgmatic compact # reclaim freed space (Borg 1.2+)
borgmatic check # verify repository and archive integrity
View archives:
borgmatic list
borgmatic list --archive latest
borgmatic info --archive latest
Step 5: Schedule with systemd Timer
Borgmatic ships a systemd service and timer unit. Enable them:
systemctl enable --now borgmatic.timer
systemctl status borgmatic.timer
The default timer runs daily at a random time within the maintenance window, preventing thundering-herd issues when many servers back up simultaneously. To customize timing:
# /etc/systemd/system/borgmatic.timer.d/override.conf
[Timer]
OnCalendar=
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=30min
systemctl daemon-reload
systemctl restart borgmatic.timer
Step 6: Restore Procedures
List available archives:
borgmatic list
Restore specific files:
borgmatic extract \
--archive webserver-2026-03-23T02:30:00 \
--path /var/www/myapp \
--destination /tmp/restore
Restore entire system to a different path:
borgmatic extract --archive latest --destination /mnt/restore
Restore a PostgreSQL database:
borgmatic restore --archive latest --database myapp_production
Mount an archive for browsing:
borgmatic mount --archive latest --mount-point /mnt/borg
ls /mnt/borg
borgmatic umount --mount-point /mnt/borg
Monitoring Hooks
Borgmatic’s hooks integrate with any monitoring system. The after_backup hook runs only on success; on_error fires on any failure:
Healthchecks.io:
hooks:
after_backup:
- curl -fsS https://hc-ping.com/your-project-uuid > /dev/null
on_error:
- curl -fsS https://hc-ping.com/your-project-uuid/fail > /dev/null
Uptime Kuma (push monitor):
hooks:
after_backup:
- curl -fsS "https://uptime.example.com/api/push/MONITOR_KEY?status=up&msg=OK" > /dev/null
PagerDuty via curl:
hooks:
on_error:
- "curl -X POST https://events.pagerduty.com/v2/enqueue -H 'Content-Type: application/json' -d '{\"routing_key\":\"YOUR_KEY\",\"event_action\":\"trigger\",\"payload\":{\"summary\":\"Backup failed\",\"severity\":\"critical\"}}'"
Database Hooks
Borgmatic coordinates database dumps with the backup process to ensure consistency:
postgresql_databases:
- name: all # dumps all databases
username: postgres
format: custom # pg_dump custom format — supports parallel restore
mysql_databases:
- name: all
username: root
password: rootpassword
options: "--single-transaction" # consistent InnoDB dump without locking
Database dumps land in a temp directory, get backed up by Borg, then are removed. Restore is a single command: borgmatic restore --archive latest.
3-2-1 Strategy with Multiple Repositories
Borgmatic natively supports multiple repositories in one config — backups run to all of them in sequence:
| Repository | Type | Location | Purpose |
|---|---|---|---|
local | Local disk | /var/backup/borg | Fast restore |
remote-lan | SSH | NAS on local LAN | Site redundancy |
remote-offsite | SSH | VPS in different datacenter | Disaster recovery |
With this config, every borgmatic run creates archives in all three locations. A disk failure, ransomware attack, or datacenter fire only affects one copy.
Comparison: Borgmatic vs Alternatives
| Feature | Borgmatic | Restic | Duplicati | Kopia | Bacula |
|---|---|---|---|---|---|
| Backend | BorgBackup | Own engine | Own engine | Own engine | Own engine |
| Encryption | AES-256-CTR | AES-256-CTR | AES-256 | AES-256-GCM | DES/AES |
| Deduplication | Yes (chunk-level) | Yes (content-addressed) | Yes (block-level) | Yes | No |
| Config format | YAML | CLI flags | GUI/XML | GUI/CLI | Config files |
| DB hooks | Native (PG, MySQL) | External scripts | No | No | Plugin |
| Append-only | Yes (SSH server-side) | No | No | No | No |
| Monitoring hooks | Yes (before/after/error) | External | External | External | Yes |
| Complexity | Low (YAML) | Medium (CLI) | Low (GUI) | Medium | High |
| Remote targets | SSH, SFTP | S3, B2, SFTP, many | FTP, S3, WebDAV | S3, B2, SFTP | LAN/WAN |
Gotchas and Edge Cases
- Passphrase loss = permanent data loss — Store the passphrase in a password manager AND export the repo key to cold storage. There is no recovery without both.
- compact is not automatic —
borgmatic compactreclaims space after pruning; without it, disk usage grows even after old archives are removed. - SSH key required for remote repos — Add the client’s public key to the backup server’s
~/.ssh/authorized_keyswithborg serverestrictions before initializing. - Clock skew causes errors — Borg requires clocks within 1 hour of each other. Use NTP (
systemctl enable --now chronyc.service). - Permissions matter — Run Borgmatic as root to back up system directories, or as the application user for application-only backups.
- First backup is slow — Deduplication only helps after the first run. Expect the initial backup to take proportionally longer.
Troubleshooting
| Problem | Solution |
|---|---|
Failed to create/acquire the lock | Previous run is stuck; run borgmatic borg break-lock /path/to/repo |
Repository is not a valid Borg repository | Wrong path or repo not initialized; run borgmatic init |
Passphrase provided in passphrase file is incorrect | Wrong env var or config passphrase; verify encryption_passphrase matches init passphrase |
Connection closed by remote host | SSH key not added or borg serve not in authorized_keys; check remote SSH config |
No archives found | First backup not yet run; run borgmatic create |
compact: error — repository does not support compaction | Borg < 1.2; upgrade BorgBackup on the repository server |
Summary
- BorgBackup provides deduplication, AES-256 encryption, compression, and append-only mode.
- Borgmatic wraps Borg in a single YAML config covering create, prune, compact, check, and hooks.
- systemd timer handles scheduling with
RandomizedDelaySecto prevent load spikes. - Database hooks ensure consistent PostgreSQL and MySQL dumps without stopping services.
- 3-2-1 strategy — three copies, two media types, one off-site — implemented by listing multiple repositories.
- Always test restores before you need them;
borgmatic checkverifies integrity but only a successful extract confirms restorability.