This article outlines how I configure BorgBackup and borgmatic on my machines.
macOS
Tested on
- MacBook Air M2
- macOS Ventura 13.4.1
- borgmatic 1.7.12 (MacPorts)
- moreutils 0.67_1 (MacPorts)
borgmatic and moreutils are also available on Homebrew.
Unlike systemd, launchctl doesn’t provide integration a syslog-like service. (I guess Apple expects you to use Console instead)
Apple’s unified logging is unreliable and in many tests I have had messages
disappearing, even when manually testing with the syslog
standard C library function.
For example, events disappear for some users when viewing them through log stream
.
If you want to redirect borgmatic’s standard out and standard error to a
$PREFIX/var/log/borgmatic/{stdout,stderr}.log
file, I recommend wrapping
borgmatic in a helper script like the following, which I have put in
$HOME/.local/bin/borgmatic_timestamp.sh
:
#!/bin/bash
# https://apple.stackexchange.com/a/406097
set -e
set -o pipefail
# Substitute /opt/local/bin/borgmatic and /opt/local/bin/ts
# for the correct path if you don't use MacPorts
/opt/local/bin/borgmatic create prune -v2 \
2> >(/opt/local/bin/ts -m '[%Y-%m-%d %H:%M:%S] -' 1>&2) \
1> >(/opt/local/bin/ts -m '[%Y-%m-%d %H:%M:%S] -')
Create a LaunchAgent in
$HOME/Library/LaunchAgents/net.yourdomain.yourprogram.plist
. Substitute
any $USER
string for your username:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>net.yourdomain.yourprogram</string>
<key>ProgramArguments</key>
<array>
<string>/Users/$USER/.local/bin/borgmatic_timestamp.sh</string>
</array>
<key>UserName</key>
<string>$USER</string>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/$USER/.local/var/log/borgmatic.stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/$USER/.local/var/log/borgmatic.stderr.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/$USER</string>
</dict>
</dict>
</plist>
Activate using
launchctl bootstrap "gui/$EUID" "$HOME/Library/LaunchAgents/net.yourdomain.yourprogram.plist"
This .plist
file configures borgmatic to run at the beginning of every hour.
If you want to run borgmatic immediately, you can kickstart it using
launchctl kickstart gui/$EUID/net.yourdomain.yourprogram.plist
This blog post is pretty helpful in explaining the new bootstrap syntax compared to the legacy load syntax using launchctl.
There’s one caveat with launchctl, when comparing it to systemd. systemd provides extensive support for process isolation and hardening. launchctl only provides limited support. It’s not possible, for example, to run a LaunchAgent and give it read-only access to the filesystem.
Restoring files
Here’s a shortcut that you can use in fish to extract a file (in this example, /etc/profile):
borgmatic extract \
--repository "$YOUR_REPOSITORY" \
--archive (
borgmatic --no-color rlist --short --repository "$YOUR_REPOSITORY" | fzf
) \
--path /etc/profile
This example uses fzf to select a specific archive. That way you can get a macOS Time Machine-like date selection, as a TUI.
Exporting keys to paper
Run the following command to show all keys for all repositories in a printable text format:
borgmatic key export --paper
You can print this file and recover your keys if you lose it. I recommend storing your paper key export in a safe location. Someone can use your paper keys and decrypt all your backups.
Validating a backup
You can stream a tar ball containing backed up data using export-tar
like so:
borgmatic export-tar \
--repository $REPOSITORY \
--archive $ARCHIVE \
--path $PATH_YOU_WANT_TO_TEST \
--destination - | \
gtar --compare --file=- -C /
This uses GNU tar (installed with MacPorts as gnutar
) to compare the files in
the piped tar ball to what’s stored on your filesystem.
Testing your backups
BorgBackup has a repository and archive consistency check command. With borgmatic, you can set this up
and run automatically in arbitrary intervals with the checks
configuration
key.
You can also perform a spot check and borgmatic supports this in version 1.8.10.
If you would like to run a sampling test yourself, you can follow these steps:
- Decide on your sample size N and sample N files from your file system.
- Export a tar archive with these files from the latest archive with
borg export-tar
repositories. - Run
tar --compare
on your file system and check the tar file contents. - Count number of differences in files to check whether backup and recovery process works correctly.
To sample, for example, 10 random files from your file system, you can combine the sort
and
awk
command in the terminal:
find / -type f -print0 | \
sort --random-sort --zero-terminated | \
awk 'BEGIN {RS="\0"} {print $0} NR==10{exit}'
Running find
on your file system can take a long time. You can speed this
up by only checking one directory. Here, find
only looks at files in your
home directory:
find $HOME -type f -print0 | \
sort --random-sort --zero-terminated | \
awk 'BEGIN {RS="\0"} {print $0} NR==10{exit}'
You may also want to exclude files that are too new to be included in a
recent BorgBackup archive. If your last backup ran one day ago, exclude
files younger than 24 hours using -ctime
:
find $HOME -type f -ctime +1 -print0 | \
sort --random-sort --zero-terminated | \
awk 'BEGIN {RS="\0"} {print $0} NR==10{exit}'
Here’s what the find
manual says on -ctime
:
-ctime n
File’s status was last changed less than, more than or exactly n*24 hours ago. […]
From the same find
manual, here’s the format for numeric arguments, such as for -ctime
:
+n for greater than n,
-n for less than n,
n for exactly n.
You can also exclude files that you aren’t backing up in BorgBackup to avoid false-positives.
If you use macOS, you can use the mdfind
command instead of find
:
mdfind -onlyin $HOME -0 \
'kMDItemContentModificationDate < $time.now(-86400)' | \
sort --random-sort --zero-terminated | \
awk 'BEGIN {RS="\0"} {print $0} NR==10{exit}'
Now that you have selected 10 files, export a tar file from your latest BorgBackup archive. You can achieve this using borgmatic export-tar
:
borgmatic export-tar \
--repository $YOUR_REPOSTIORY \
--archive latest \
--path $PATHS
In bash, $PATHS
should be an array containing pairs of --path
and the path
itself. Here’s how to do that with a combination of mapfile
and the awk
command:
mapfile -d '' PATHS < <(find $HOME -type f -ctime +1 -print0 | \
sort -z -R | \
awk 'BEGIN {RS="\0"}
{printf("--path%c%s%c", 0, $0, 0)}
NR==10 {exit}
')
With mdfind
on macOS, you can run this instead:
mapfile -d '' PATHS < <(mdfind -onlyin $HOME -0 \
'kMDItemContentModificationDate < $time.now(-86400)' | \
sort --random-sort --zero-terminated | \
awk 'BEGIN {RS="\0"}
{printf("--path%c%s%c", 0, $0, 0)}
NR==10 {exit}
')
Now run export-tar
on these files:
borgmatic export-tar \
--repository $YOUR_REPOSITORY \
--archive latest \
"${PATHS[@]}" \
--destination $PWD/archive.tar
Compare the contents using the tar --compare
command:
tar --compare --file archive.tar --verbose --directory /
This tar
invocation lists each file in the archive and tells you whether there are any
differences to the files on your file system. tar
also checks whether
file permissions or timestamps are different.
You can then estimate the total amount of differences in your file system
based on how many of the 10 files in the archive.tar
file were different.
BorgBackup or Time Machine
I use Time Machine to back up my MacBook computers. Even though Time Machine is fast and supports incremental backups, it doesn’t work well with offsite backups. With BorgBackup, I can reuse my configuration between my NixOS machines and my MacBook computers and perform backups on-site and off-site.
Instead of preferring one method over the other, I choose to use both BorgBackup and Time Machine. Further, if you follow the 3-2-1 Backup Rule, having your backups on different backup media and off-site gives you peace of mind.
For creating off-site backups with BorgBackup, I use BorgBase.