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/sdb /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