SpareShelf

18 October 2026 · shnele

A 3-2-1 backup that I actually test

For years my backup strategy was a cron job I had written once and never looked at again. It ran every night, it printed nothing, and I assumed that silence meant success. When a disk finally died I found out the job had been failing since a hostname change eleven months earlier.

The setup I run now is boring on purpose. Three copies, two kinds of media, one of them off the shelf and out of the flat.

Home serverlive data USB disknightly Object storagenightly, encrypted Restore drill1st of the month
Two independent repositories. The drill restores from a randomly chosen one.

The nightly job

Each repository gets its own password file and its own systemd timer, so one broken target cannot block the other.

restic -r /mnt/usb/restic backup /srv/data /etc \
  --exclude-caches --one-file-system --tag nightly
restic -r /mnt/usb/restic forget --tag nightly \
  --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --prune

The unit has OnFailure= pointing at a tiny service that sends me a push notification. Silence is no longer treated as success: a second timer checks that the newest snapshot is younger than 36 hours and complains if it is not.

The restore drill

On the first of every month a script picks a snapshot, restores one directory into a scratch location and compares checksums against the live copy for files that have not changed since. It takes four minutes and it has already caught one problem — a USB enclosure that silently dropped to USB 2.0 speeds and made the nightly run overlap with the next one.

RepositorySizeNightly runFull restore
USB disk212 GB3–6 min~50 min
Object storage214 GB5–11 min~4 h on my uplink

A backup you have never restored from is a hope, not a backup.

That four-hour number is the one worth knowing in advance. It is fine for photos. It would not be fine for anything I needed the same evening, which is why the USB disk exists at all.