Borgmatic User Data Backup

Posted on 3 2026

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.