Borgmatic User Data Backup
The user data backup covers the home directory. This is the backup that runs every day and protects everything that would be genuinely painful to lose: documents, code, configuration, keys, databases, years of accumulated work. It runs as your regular user account, not root, and targets a dedicated repository separate from the system configuration backup.
The approach is the same as the system backup: BorgBackup for the repository format, borgmatic for automation, two destinations for redundancy.
Borg preparation
Create the per-user Borg configuration directories:
mkdir -p ~/.config/borg/{keys,ssh,security}
chmod -R 0700 ~/.config/borg
mkdir -p ~/.cache/borg
Generate a dedicated SSH key for the user backup. This is separate from your personal SSH key and separate from the system backup key:
ssh-keygen -t ed25519 \
-C "BorgBackup-user@$(hostname -f)" \
-f ~/.config/borg/ssh/id_ed25519 \
-N ""
chmod 0600 ~/.config/borg/ssh/id_ed25519
Display the public key:
cat ~/.config/borg/ssh/id_ed25519.pub
Install this public key on the NAS backup server. As with the system backup key, the NAS must set up a forced command that restricts this key to Borg operations only and points it at this user’s repository path. The NAS section covers the server-side configuration.
Verify connectivity to the local NAS:
ssh -i ~/.config/borg/ssh/id_ed25519 borg-backup@nas.yourdomain.net
And to the remote NAS at The Lighthouse via the VPN:
ssh -i ~/.config/borg/ssh/id_ed25519 borg-backup@lighthouse-nas.yourdomain.net
Both should connect and immediately close, confirming the forced command is working.
Generate a passphrase
The user data backup uses keyfile-blake2 encryption, which stores the key in a local keyfile rather than in the repository. This means the repository cannot be decrypted without both the keyfile and the passphrase, which is a stronger guarantee than repokey alone.
Generate a seven-word Diceware passphrase:
xkcdpass --numwords 7
Store it in KeePassXC under Infrastructure > Backup > User Backup Repository Passphrase. This passphrase, along with the keyfile at ~/.config/borg/keys/, is what stands between your data and an inaccessible repository. Both must be backed up.
Mail notification script
Create the per-user notification script:
mkdir -p ~/.config/borgmatic
tee ~/.config/borgmatic/notify.sh << 'EOF'
#!/usr/bin/env bash
#
# Notify user of borgmatic backup error.
# Called by borgmatic with: notify.sh "{configuration_filename}" "{repository}" "{error}" "{output}"
#
mail -s "Borgmatic User Backup Error on $(hostname -s)" "${USER}" <<MAIL
Borgmatic user data backup on $(hostname -f) failed.
Configuration file: $1
Repository: $2
Error message: $3
Command output:
$4
MAIL
EOF
chmod 0700 ~/.config/borgmatic/notify.sh
Borgmatic configuration
Create ~/.config/borgmatic/user.yaml using the current borgmatic v1.8+ flat schema:
# ~/.config/borgmatic/user.yaml
# User data backup for desktop home directory
# ============================================================
# What to back up
# ============================================================
source_directories:
- ~/
exclude_patterns:
# Cache directories
- '**/.cache'
- '**/cache'
- '**/__pycache__'
- '**/.mypy_cache'
- '**/.pytest_cache'
- '**/.tox'
# Build artefacts
- '**/node_modules'
- '**/target'
- '**/.gradle'
- '**/.m2'
# Large media that lives elsewhere or can be redownloaded
- ~/Downloads
- ~/Music
- ~/Videos
- ~/.local/share/Trash
- ~/.thumbnails
- ~/.Trash
# KDE-specific cache and volatile data
- ~/.local/share/baloo
- ~/.local/share/akonadi
- ~/.local/share/kwalletd
- ~/.local/share/recently-used.xbel
# Virtual machine images (back up separately if needed)
- '**/*.vmdk'
- '**/*.vdi'
- '**/*.qcow2'
exclude_caches: true
exclude_if_present:
- .nobackup
# ============================================================
# Where to store backups
# ============================================================
repositories:
- path: ssh://borg-backup@nas.yourdomain.net/volume1/BorgBackup/{user}-{hostname}
label: local-nas
- path: ssh://borg-backup@lighthouse-nas.yourdomain.net/volume1/BorgBackup/{user}-{hostname}
label: remote-nas
encryption_passphrase: "your seven word diceware passphrase here"
ssh_command: ssh -i ~/.config/borg/ssh/id_ed25519 -o BatchMode=yes -o VerifyHostKeyDNS=yes -o StrictHostKeyChecking=yes
archive_name_format: '{user}-{hostname}-{now}'
compression: zstd
# ============================================================
# Retention policy
# ============================================================
keep_within: 6H
keep_hourly: 8
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
keep_yearly: 1
# ============================================================
# Consistency checks
# ============================================================
checks:
- name: repository
frequency: 2 weeks
- name: archives
frequency: 4 weeks
# ============================================================
# Error hooks
# ============================================================
on_error:
- ~/.config/borgmatic/notify.sh "{configuration_filename}" "{repository}" "{error}" "{output}"
Secure and validate the configuration
The configuration contains the passphrase in plaintext. Lock it down:
chmod 0600 ~/.config/borgmatic/user.yaml
Validate the configuration:
borgmatic config validate --config ~/.config/borgmatic/user.yaml
Initialise the repositories
The user backup uses keyfile-blake2 encryption, which generates a local keyfile in ~/.config/borg/keys/:
borgmatic init \
--config ~/.config/borgmatic/user.yaml \
--encryption keyfile-blake2 \
--verbosity 1
Borgmatic initialises both repositories in sequence.
Back up the keyfile immediately
The keyfile is required to decrypt the repository. Without it, the passphrase alone is not enough. Back it up now before running a single backup:
ls -la ~/.config/borg/keys/
Copy the keyfile to your offline safe storage:
cp ~/.config/borg/keys/* /media/${USER}/SafeStorage/BorgBackup/
Also store it in KeePassXC as a file attachment under Infrastructure > Backup > User Backup Keyfile.
Run an interactive backup test
Run the first backup manually with verbose output:
borgmatic create \
--config ~/.config/borgmatic/user.yaml \
--verbosity 1 \
--list \
--stats
The first run transfers everything. Subsequent runs transfer only changes. Deduplication means the second and subsequent daily backups are typically a fraction of the size of the first.
List archives to confirm the backup was created:
borgmatic list --config ~/.config/borgmatic/user.yaml
Test extracting a single file:
borgmatic extract \
--config ~/.config/borgmatic/user.yaml \
--archive latest \
--path home/${USER}/.bashrc \
--destination /tmp/borg-test
cat /tmp/borg-test/home/${USER}/.bashrc
rm -rf /tmp/borg-test
Scheduling via anacron
Add the user backup to the per-user anacron daily jobs established earlier in this series:
tee ~/.anacron/cron.daily/borgmatic-user << 'EOF'
#!/usr/bin/env bash
# Daily user data backup via borgmatic
# Do not run on battery power
if command -v on_ac_power >/dev/null 2>&1; then
on_ac_power || exit 0
fi
borgmatic create \
--config "${HOME}/.config/borgmatic/user.yaml" \
--syslog-verbosity 1 \
--stats
EOF
chmod 0755 ~/.anacron/cron.daily/borgmatic-user
This integrates with the per-user anacron timer set up in the anacron section, which fires on login and hourly. The backup runs once daily when the machine is on mains power.
Checking backups
Logs
journalctl --user -t borgmatic --since "7 days ago"
Listing archives
borgmatic list --config ~/.config/borgmatic/user.yaml
Archive information and statistics
borgmatic info \
--config ~/.config/borgmatic/user.yaml \
--archive latest
Consistency check
Run manually at any time to verify repository integrity:
borgmatic check \
--config ~/.config/borgmatic/user.yaml \
--verbosity 1
Restoring files
Mounting a backup archive
The easiest way to access backed-up files is by mounting the archive as a filesystem. This allows browsing and copying individual files without extracting the entire archive.
mkdir -p /media/${USER}/Borg-Backup
borgmatic mount \
--config ~/.config/borgmatic/user.yaml \
--mount-point /media/${USER}/Borg-Backup
The archive is now accessible in Dolphin as a mounted location. Browse to /media/username/Borg-Backup/ to find your files organised by archive name.
To mount a specific archive rather than all archives:
borgmatic mount \
--config ~/.config/borgmatic/user.yaml \
--archive latest \
--mount-point /media/${USER}/Borg-Backup
To mount from a specific repository only:
borgmatic mount \
--config ~/.config/borgmatic/user.yaml \
--repository local-nas \
--archive latest \
--mount-point /media/${USER}/Borg-Backup
When done:
borgmatic umount \
--mount-point /media/${USER}/Borg-Backup
rmdir /media/${USER}/Borg-Backup
Extracting specific files
To extract files directly without mounting:
borgmatic extract \
--config ~/.config/borgmatic/user.yaml \
--archive latest \
--path home/${USER}/Documents/important-file.txt \
--destination ~/restored
Full home directory restore
In a disaster scenario where the home directory needs to be restored from scratch, boot from a Kubuntu live USB, connect to the network and VPN, install borgbackup, and restore:
sudo apt install borgbackup
# Restore the keyfile first (from offline safe storage or KeePassXC)
mkdir -p ~/.config/borg/keys
cp /path/to/saved/keyfile ~/.config/borg/keys/
# Extract the home directory from the remote repository
borg extract \
--progress \
ssh://borg-backup@lighthouse-nas.yourdomain.net/volume1/BorgBackup/yourusername-hostname::latest
The remote NAS is used for disaster recovery on the assumption that if you need a full restore, the local NAS may also be unavailable.
The keyfile at
~/.config/borg/keys/is as important as the passphrase. Without it, the repository cannot be decrypted regardless of whether the passphrase is correct. If you lose both the keyfile and its backup, the data in the repository is unrecoverable. Store the keyfile backup somewhere physically separate from the machine.