Server — Backup — Install
The previous article covered installing Borgbackup and borgmatic on February, writing the initial config, and running the first backup manually. This article covers the rest: running borgmatic automatically on a schedule, adding hooks so you know when a backup fails, and confirming the whole thing is working without you having to think about it.
The goal by the end of this article is a backup that runs daily, logs to the journal, and sends a notification if it fails. That is the minimum bar for a backup setup you can actually rely on.
The systemd service
borgmatic ships with sample systemd unit files, and the approach they use is worth following closely. Two files are needed: a service that runs borgmatic, and a timer that decides when.
Create /etc/systemd/system/borgmatic.service:
[Unit]
Description=borgmatic backup
Wants=network-online.target
After=network-online.target
ConditionACPower=true
[Service]
Type=oneshot
Nice=19
CPUSchedulingPolicy=batch
IOSchedulingClass=best-effort
IOSchedulingPriority=7
IOWeight=100
Restart=no
LogRateLimitIntervalSec=0
ExecStart=systemd-inhibit --who="borgmatic" --why="Prevent interrupting scheduled backup" /root/.local/bin/borgmatic --syslog-verbosity 1
A few things worth noting here. ConditionACPower=true prevents borgmatic from running if February is on battery power, which pairs well with the UPS setup: if the power has gone out, the last thing you want is a backup saturating the disk right as February is trying to shut down gracefully.
Nice=19 and the IO scheduling flags push borgmatic to the bottom of the priority queue so it does not compete with anything else February is doing during the backup window. Backups can be slow; there is no reason for them to be disruptive.
systemd-inhibit prevents the system from sleeping or shutting down mid-backup. On a server that is always on this is mostly theoretical, but it costs nothing to include.
The borgmatic binary path /root/.local/bin/borgmatic reflects the pipx installation from the previous article. If you installed borgmatic differently, adjust accordingly. You can confirm the path with sudo which borgmatic.
The systemd timer
Create /etc/systemd/system/borgmatic.timer:
[Unit]
Description=Run borgmatic backup daily
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1h
[Install]
WantedBy=timers.target
OnCalendar=daily runs the backup once every 24 hours. Persistent=true means if February was off when the scheduled time passed, systemd will run the backup as soon as the server comes back up. RandomizedDelaySec=1h adds a random delay of up to an hour, which avoids all your infrastructure hitting the NAS simultaneously if you ever run borgmatic on multiple machines.
If you want the backup at a specific time rather than midnight, replace OnCalendar=daily with something like OnCalendar=*-*-* 02:00:00 for 2am.
Enable and start the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now borgmatic.timer
Confirm it is active:
sudo systemctl status borgmatic.timer
The output should show the timer as active and list the next trigger time. You can also see all active timers with sudo systemctl list-timers, which is useful for confirming borgmatic appears in the list alongside its next run time.
Hooks and notifications
borgmatic’s hooks system lets you run commands before and after backups, and crucially, on error. The on_error hook is what turns a silent failure into something you actually find out about.
Add a hooks section to /etc/borgmatic/config.yaml:
hooks:
before_backup:
- echo "Starting borgmatic backup on $(date)"
after_backup:
- echo "borgmatic backup completed successfully on $(date)"
on_error:
- echo "borgmatic backup FAILED on $(hostname) at $(date)" | mail -s "Backup failure: February" you@example.com
The before_backup and after_backup hooks log to the journal via stdout, which is picked up by the systemd service and visible in journalctl. The on_error hook sends an email.
As with the UPS notification script, this assumes mail is available and configured on February. If you are routing notifications through a webhook or ntfy instead, replace the mail command with a curl call:
on_error:
- >
curl -s -X POST https://ntfy.sh/your-topic
-H "Title: Backup failure on February"
-d "borgmatic failed at $(date). Check journalctl -u borgmatic."
The > YAML block scalar lets the curl command span readable lines in the config without needing shell line continuation. Keep it to one logical command per hook entry.
Checking the logs
borgmatic logs to the systemd journal through the service unit. To see the output of the last backup run:
sudo journalctl -u borgmatic --no-pager -n 100
To follow a backup in progress:
sudo journalctl -u borgmatic -f
To see the full history of backup runs and their outcomes:
sudo journalctl -u borgmatic --no-pager
A successful borgmatic run will end with lines showing the prune and check steps completing without errors, followed by the after_backup hook output.
Triggering a manual run
At any point you can run borgmatic manually outside the timer schedule:
sudo borgmatic create --verbosity 1 --stats
This is useful after making config changes, after adding new source directories, or any time you want to force a backup without waiting for the next scheduled run. The --stats flag shows a summary of what was transferred and the current state of the repository.
To run only a specific borgmatic action without the full create/prune/check cycle:
sudo borgmatic prune --verbosity 1
sudo borgmatic check --verbosity 1
Confirming the schedule is working
After the first timer-triggered run completes, confirm it appeared in the journal:
sudo journalctl -u borgmatic --since yesterday
And confirm the archive was created on the NAS side:
sudo borgmatic list
The archive list should now show two entries: the one you created manually in the previous article, and the one the timer just ran. If you only see one, the timer has not fired yet, or something went wrong. Check the timer status and the journal.
What is next
borgmatic is now running on a schedule, logging to the journal, and notifying on failure. The remaining article in this series covers testing restores: mounting the backup repository as a filesystem, extracting specific files, and verifying that what went in can actually come back out again.
That article exists because the most common reason a backup fails when you need it is not that the backup never ran. It is that you assumed it was fine and never checked.