2025-01-31 BorgBackup for the laptop and the server
A few days ago I mentioned the rsync-based backup script I use to copy data from the server to the laptop.
The backup script that makes backups of my laptop (including the backup from the server) to external disks is different, however.
My actual backup uses external disks. I have two of them. One of them is always at the office. Every now and then (not often enough!) I make a backup to the external disk I have at hand and then I carry it to the office and bring the other one back. That is, the laptop and the two external disks are never in the same location.
The two external disks I use are "known backup disks". When I plug them in, BorgBackup immediately starts making a backup. I discussed this setup back in 2017.
I'm just going to repost the stuff and I don't think there have been any significant changes.
Run commands and create files as root.
Either use `sudo` for every command you run, or use `sudo su` once and do everything as root. No matter how you do it, the danger zone awaits!
`/mnt/backup`
Create the mount point on the laptop:
mkdir /mnt/backup
`/etc/fstab`
Any disk labeled “Backup” will be mounted as `/mnt/backup`:
LABEL=Backup /mnt/backup auto nosuid,nodev,nofail,noauto,x-gvfs-show 0 0
`/etc/backups/40-backup.rules`
Install a symlink to this file:
ln -s /etc/backups/40-backup.rules \
/etc/udev/rules.d/40-backup.rules
The file content:
ACTION=="add", SUBSYSTEM=="bdi", DEVPATH=="/devices/virtual/bdi/*", TAG+="systemd", ENV{SYSTEMD_WANTS}="automatic-backup.service"
This makes sure that the service `automatic-backup` starts whenever an external disk gets plugged into the laptop.
`/etc/backups/automatic-backup.service`
Install a symlink to this file:
ln -s /etc/backups/automatic-backup.service \
/etc/systemd/system/automatic-backup.service
The service does nothing except start the `run.sh` shell script.
[Service] Type=oneshot ExecStart=/etc/backups/run.sh
`/etc/backups/backup.disks`
The `run.sh` below runs for every external disk plugged in. It's important to only make backups to known backup disks, however.
The known backup disks are identified by their `uuid` in the `backup.disks` file.
To list the disk labels and their `uuid`:
lsblk -o+uuid,label
Add the `uuid` to the `/etc/backups/backup.disks` file.
This is the content of the file identifying my backup disks, for example:
# generate using: lsblk -o+uuid,label 7c478832-5d7f-43d3-9b79-20cfc67fb0e6 bb1c034e-dd21-48e7-a496-1b95685a094d 156cf4df-aa58-421e-b3d0-583fe6fdff4a
If you create your copy of the file, your uuids will be different!
When you run `lsblk -o+uuid,label` you'll also see the device itself. In my case, that's `/dev/sdb`. Create a BorgBackup repository on the new disk, mount it, create the directory and initialize the repository.
When initializing the repository, you're asked for a passphrase. You will need it for the `run.sh` script.
mount /dev/sdb1 /mnt/backup mkdir /mnt/backup/borg-backups borg init --encryption=repokey --progress /mnt/backup/borg-backups/backup.borg umount /mnt/backup
Make sure the disk has the correct label! You can do this from the command-line but I used Disks.
`/etc/backups/run.sh`
Now we're ready for the actual script. It checks whether a known backup disk is mounted under `/mnt/backup` and if so, it creates a new archive in the repository.
Don’t forget to search for `BORG_PASSPHRASE` and change it to whatever you used when you ran `borg init` above!
Hide the passphrase from everybody else and to make it executable:
chmod 0700 run.sh
This the content of the shell script:
#!/bin/bash -ue
# The udev rule is not terribly accurate and may trigger our service before
# the kernel has finished probing partitions. Sleep for a bit to ensure
# the kernel is done.
#
# This can be avoided by using a more precise udev rule, e.g. matching
# a specific hardware path and partition.
sleep 5
#
# Script configuration
#
# The backup partition is mounted there
MOUNTPOINT=/mnt/backup
# This is the location of the Borg repository
TARGET=$MOUNTPOINT/borg-backups/backup.borg
# This is the file that will later contain UUIDs of registered backup drives
DISKS=/etc/backups/backup.disks
# Find whether the connected block device is a backup drive
for uuid in $(lsblk --noheadings --list --output uuid)
do
if grep --quiet --fixed-strings $uuid $DISKS; then
break
fi
uuid=
done
if [ ! $uuid ]; then
echo "No backup disk found, exiting"
exit 0
fi
echo "Disk $uuid is a backup disk"
partition_path=/dev/disk/by-uuid/$uuid
# Mount file system if not already done. This assumes that if something is already
# mounted at $MOUNTPOINT, it is the backup drive. It won't find the drive if
# it was mounted somewhere else.
(mount | grep $MOUNTPOINT) || mount $partition_path $MOUNTPOINT
drive=$(lsblk --inverse --noheadings --list --paths --output name $partition_path | head --lines 1)
echo "Drive path: $drive"
#
# Create backups
#
# Set BORG_PASSPHRASE or BORG_PASSCOMMAND somewhere around here, using export,
# if encryption is used.
export BORG_PASSPHRASE="*secret*"
# No one can answer if Borg asks these questions, it is better to just fail quickly
# instead of hanging.
export BORG_RELOCATED_REPO_ACCESS_IS_OK=no
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no
echo Backup `hostname`
borg create \
--stats \
--one-file-system \
--compression lz4 \
--show-version \
--exclude /proc \
--exclude /dev \
--exclude /sys \
--exclude /tmp \
--exclude /mnt \
--exclude /media \
--exclude /home/alex/.cache \
--exclude /root/.cache \
--exclude /var/cache \
--exclude /var/log \
--exclude /run/user/1000/gvfs \
$TARGET::{utcnow}-{hostname} \
/
echo Prune `hostname`
borg prune \
--stats \
--list \
--show-rc \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--glob-archives '*-{hostname}' \
$TARGET
# Just to be completely paranoid
sync
if [ -f /etc/backups/autoeject ]; then
umount $MOUNTPOINT
hdparm -Y $drive
fi
if [ -f /etc/backups/backup-suspend ]; then
systemctl suspend
fi
Check the logs!
Check the log while the backup is being written:
journalctl --follow --unit automatic-backup
Make sure the backup disk is identified correctly and look at the sizes of the archives. If they're too small, check whether the archives contain the right files!
#Backup #Administration
- *2025-02-04**. A reader sent me an email saying: “you may want to call `borg compact` after `borg prune` in order to actually free up the disk space corresponding to the pruned archives. Otherwise your backup repo will keep growing in size.”
Good point. I think in my case it doesn’t matter because I don’t use the disk space for anything else (and surely Borg will reuse it). But who knows. Your situation might be different.
- *2025-05-09**. As I used this setup on a second laptop, I realised that the order of instructions are sometimes in the wrong order (like execute permission of the script before the contents of the script). Sorry! 😅
- *2025-06-01**. Hm, something still doesn't work. When I plug the disk into the second laptop, nothing happens. It's as if the udev rule doesn't get consulted. When I run `sudo systemctl start automatic-backup.service` the backup gets written.
- *2025-07-11**. Oh, found it. The link from `/etc/udev/rules.d/40-backup.rules` → `/etc/backups/40-backup.rules` was there and the file `/etc/backups/40-backup-rules` existed.
Did you notice the typo? Today I renamed `40-backup-rules` to `40-backup.rules`. Let's hope that solves the issue.
- *2025-08-16**. I recently upgraded from Debian Bookworm (12) to Trixie (13). And I ran out of disk space!
Aug 16 13:05:15 melanobombus systemd[1]: Starting automatic-backup.service... Aug 16 13:05:20 melanobombus run.sh[51470]: Disk 7c478832-5d7f-43d3-9b79-20cfc67fb0e6 is a backup disk Aug 16 13:05:20 melanobombus run.sh[51470]: Drive path: /dev/sdb1 Aug 16 13:05:20 melanobombus run.sh[51470]: Backup melanobombus Aug 16 13:05:20 melanobombus run.sh[51503]: borgbackup version 1.4.0 Aug 16 13:31:14 melanobombus run.sh[51503]: Insufficient free space to complete transaction (required: 1.89 GB, available: 0 B). Aug 16 13:31:14 melanobombus systemd[1]: automatic-backup.service: Main process exited, code=exited, status=2/INVALIDARGUMENT Aug 16 13:31:14 melanobombus systemd[1]: automatic-backup.service: Failed with result 'exit-code'. Aug 16 13:31:14 melanobombus systemd[1]: Failed to start automatic-backup.service. Aug 16 13:31:14 melanobombus systemd[1]: automatic-backup.service: Consumed 15min 46.268s CPU time, 4.6G memory peak.
`df -h` reports:
/dev/sdb1 916G 863G 7.2G 100% /mnt/backup
So now:
set MOUNTPOINT /mnt/backup set TARGET $MOUNTPOINT/borg-backups/backup.borg sudo borg compact $TARGET
Let's see if that frees up some space. Otherwise, I'll have to reduce the number of archives kept.
New:
/dev/sdb1 916G 801G 70G 93% /mnt/backup