Databases are the most dangerous data to back up directly: their files change while the backup is running. This episode teaches the correct pattern — creating a consistent dump with pg_dump/mysqldump via before/after backup hooks, borgmatic's native database support, the WAL concept, and the file-based service case.

In episode 9 we automated backups with borgmatic. Now the trickiest challenge: databases. Copying raw database data files is like photographing a book while it is being read — half of the text has moved, and the consistency is broken. A database backup must be made through a mechanism that guarantees consistency: a dump or a snapshot that accounts for the WAL. This episode teaches the correct pattern for PostgreSQL, MySQL/MariaDB, and file-based services.
Databases write pages of data to disk continuously. If a backup copies database files while a transaction is in progress, the result can be a mix of mutually inconsistent page versions — a restore yields a corrupt database.
pg_dump/mysqldump): reads the database through the database API with a read transaction — producing a text/SQL file that is consistent at one point in time. This is the simplest and most common way.The practical recommendation for most workloads: dump first, then back up the dump file. Borg does not need to know it is a database — it simply stores the consistent dump file.
Step 1 — make sure the dump directory is in source_directories:
source_directories:
- /home
- /etc
- /var/backups/dbStep 2 — define the hooks:
hooks:
before_backup:
- pg_dump -U backup_user appdb | gzip > /var/backups/db/appdb.sql.gz
- mysqldump --all-databases --single-transaction | gzip > /var/backups/db/mysql.sql.gz
after_backup:
- rm -f /var/backups/db/appdb.sql.gz /var/backups/db/mysql.sql.gz
on_error:
- echo "Database backup FAILED" >&2The flow: the dump is created → borgmatic backs up the /var/backups/db directory → the after_backup hook cleans up the dumps so they do not pile up.
pg_dump -U backup_user appdb — consistent per-database. For the whole cluster, pg_dumpall. Make sure the backup_user has read rights (SELECT).mysqldump --all-databases --single-transaction — --single-transaction gives a consistent snapshot for InnoDB without locking all tables.pg_dump/mysqldump are on the PATH of the user running borgmatic (root is usually fine).Without borgmatic, the same pattern is achieved with a shell script + cron:
#!/usr/bin/env bash
set -euo pipefail
pg_dump -U backup_user appdb | gzip > /var/backups/db/appdb.sql.gz
mysqldump --all-databases --single-transaction | gzip > /var/backups/db/mysql.sql.gz
export BORG_REPO=/backup/borg
export BORG_PASSCOMMAND='cat /root/.borg-passphrase'
borg create --stats /backup/borg::"{hostname}-{now}" /home /etc /var/backups/db
borg prune --keep-daily 7 --keep-weekly 4 --keep-monthly 6
borg compact
rm -f /var/backups/db/appdb.sql.gz /var/backups/db/mysql.sql.gzBorgmatic also has built-in database support — it manages the dump and restore itself without manual hooks:
postgresql_databases:
- name: appdb
hostname: localhost
username: backup_user
mysql_databases:
- name: allWith this configuration, borgmatic creates the database dump to a temporary location, backs it up, then cleans it up — plus the borgmatic restore --database capability for targeted restores. To begin with, the manual hook pattern is more explicit; the native support is more concise and documented.
Important
Database backup commands must run as a user with sufficient database read access, and ideally not as superuser. Create a dedicated backup_user with minimal privileges — one example of the least-privilege principle that applies to Borg too.
For services whose data is files (Nextcloud, GitLab, PHP applications) there are different nuances:
occ maintenance:mode --on during the backup.source_directories — pointless.after_backup.before_backup creates the dump, after_backup cleans up.pg_dump for PostgreSQL; mysqldump --single-transaction for MySQL/MariaDB.postgresql_databases and mysql_databases support.In episode 11 we take the backup off the server: Borg on a Remote Server (SSH) — a repository on another host via borg serve, SSH key setup with a restricted command, and security practices such as a dedicated passwordless user and Borg's built-in seccomp.