I have finally set up backups on my server
This procedure was scary and felt like stumbling around a maze in a blindfold. Borg is way too powerful and customizable for my needs. I think it works. We’ll see.
This post is a part of my Homelab Series. See the index here.
Installation
apt update
apt install pipx
pipx ensurepath
apt install borgbackup
pipx install borgmatic
Check if it’s installed:
borgmatic --version
Configuration
Generate the config file:
borgmatic config generate --destination ~/.config/borgmatic/config.yaml
I don’t know why borgmatic didn’t pick up on the custom --destination
, so I had to fix it, caveman-style:
sudo mkdir /etc/borgmatic
sudo ln -s ~/.config/borgmatic/config.yaml /etc/borgmatic/config.yaml
Here’s my config:
source_directories:
- /home/pe8er
- /mnt/ssd-sandisk
repositories:
- path: /mnt/media/borgbackups
label: g3-backup
encryption: repokey-blake2
# - path: ssh://[email protected]/./repo < HETZNER GOES HERE once I buy it
exclude_patterns:
- /home/pe8er/.cache
- /home/pe8er/Downloads
- /home/pe8er/Movies
- /home/pe8er/TV
- /home/pe8er/snap
- '*/*tmp'
- '*/log'
- '*/logs'
- '*/*.log'
- '*/.stversions'
- '*/.vscodium-server'
- '*/.local'
- '*/.npm'
- '*/.ssh'
- '*/.secrets'
encryption_passcommand: cat /home/pe8er/.secrets/borg
keep_daily: 7
keep_weekly: 4
keep_monthly: 3
keep_yearly: 1
# keep_13weekly: 13
# keep_3monthly: 3
ntfy:
topic: borg
server: http://192.168.1.199:8787
username: NTFY-USERNAME
password: NTFY-PASSWORD
finish:
title: A borgmatic backup completed successfully
message: Nice!
tags: borgmatic,+1
priority: min
fail:
title: A borgmatic backup failed
message: You should probably fix it
tags: borgmatic,-1,skull
priority: max
states:
- finish
- fail
Holy shit, the way keep_* options are designed is absolutely beyond my comprehension.
Validate config:
borgmatic -v 2 config validate
Launch the Borg!!!
First, initiate the repository:
borgmatic init --encryption repokey-blake2
(Apparently, repokey-blake2
is the fastest option on an Intel CPU). Once initiated, make sure to export the repository key and place it outside the repository:
borg key export /mnt/media/borgbackups/ ~/.secrets/borgrepokey
And create the backup!
borgmatic create --verbosity 2 --list --stats
Oh damn it works! And it’s pretty damn fast.
g3-backup: Pruning archives
Keeping archive (rule: daily #1): g3-2025-07-11T14:50:55.642919 Fri, 2025-07-11 14:50:55 [1678cb92d646bca4a8912c4479ab9d96a9f3c38f81e8a26bdbcd79dbdd2f95cd]
Pruning archive (1/1): g3-2025-07-11T14:49:49.867765 Fri, 2025-07-11 14:49:50 [35cef225b9c6a0bcf06fb4c228166e158f3f602f6cf6bf300dbd14fe827fdeeb]
Keeping archive (rule: daily[oldest] #2): g3-2025-07-11T10:43:31.991167 Fri, 2025-07-11 10:43:32 [9248f2607a766f9bbb686173cdc1ae59df100cc54ec8155f16e1fffb4516e9d1]
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
Deleted data: -738.30 GB -715.41 GB -4.71 MB
All archives: 1.48 TB 1.43 TB 642.51 GB
Unique chunks Total chunks
Chunk index: 364881 831003
------------------------------------------------------------------------------
g3-backup: Compacting segments
compaction freed about 4.71 MB repository space.
g3-backup: Running consistency checks
g3-backup: Skipping repository check due to configured frequency; 29 days, 22:33:44.729506 until next check (use --force to check anyway)
g3-backup: Skipping archives check due to configured frequency; 29 days, 22:33:44.728086 until next check (use --force to check anyway)
summary:
/etc/borgmatic/config.yaml: Successfully ran configuration file
Automation
Create service file:
sudo nano /etc/systemd/system/borgmatic.service
[Unit]
Description=borgmatic backup
Wants=network-online.target
After=network-online.target
ConditionACPower=true
[Service]
Type=oneshot
LockPersonality=true
MemoryDenyWriteExecute=no
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
ProtectSystem=full
CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_NET_RAW
Nice=19
CPUSchedulingPolicy=batch
IOSchedulingClass=best-effort
IOSchedulingPriority=7
IOWeight=100
Restart=no
LogRateLimitIntervalSec=0
ExecStartPre=sleep 1m
ExecStart=systemd-inhibit --who="borgmatic" --why="Prevent interrupting scheduled backup" /home/pe8er/.local/bin/borgmatic --verbosity 2 --list --stats --syslog-verbosity 2
Test it:
sudo systemctl start borgmatic.service
journalctl -u borgmatic -f
Create and enable timer service:
sudo nano /etc/systemd/system/borgmatic.timer
[Unit]
Description=Run Borgmatic Backup
[Timer]
OnCalendar=*-*-* 4:00:00
Persistent=true
[Install]
WantedBy=timers.target
systemctl enable --now borgmatic.timer
Does it work?
systemctl list-timers
journalctl -u borgmatic -f
Browsing and Restoring Files
List backups:
sudo borgmatic list
Perform pruning, compaction, create backups according to your config file, and check backups for consistency:
sudo borgmatic --verbosity 2 --list --stats
Search for a file:
sudo borgmatic list --find foo.txt
Extract a file:
sudo borgmatic extract --archive latest --path "home/pe8er/bin/foo.txt"
Warning: Borgmatic understands file paths extremely literally, which means for the above to work, I need to cd
all the way to the top level directory, otherwise I will end up with /home/pe8er/bin/home/pe8er/bin/foo.txt
. How dumb is that.
Mount a backup:
sudo borgmatic list
g3-backup: Listing archives
g3-2025-07-16T10:00:37.300055 Wed, 2025-07-16 10:00:38 [c1a5caa39e9b191a95b7a32de954fcba5ab26858700cf9b9058d6545d73604e2]
g3-2025-07-16T16:46:19.923572 Wed, 2025-07-16 16:46:20 [fe6fce96d5e030b74dc9f6b63697923945bccf2a60af03c059d916f09cc3ee61]
# mount last backup to ~/mount
mkdir -p ~/mount
sudo borg mount -o uid=1000,gid=1000 /mnt/media/borgbackups/::g3-2025-07-16T10:00:37.300055 ~/mount
Note: Since systemd runs as root, mounted backup is not readable to me. Duh. I need to experiment with -o uid=1000,gid=1000
or -o ignore_permissions
once the backup finishes.
Thanks to André Sterba for a tutorial that makes much more sense to me than the official documentation..