For a homelab, 3-2-1 — three copies, two different media, one off-site — translates to one concrete pattern: your live data, a nightly restic snapshot to a second local disk or box, and the same data in Backblaze B2 at $6/TB/month. For a typical 150GB of irreplaceable data that's about $1 a month and thirty lines of script. The rule that outranks the numbers: a backup you haven't restored from is a hope, not a backup, and most self-hosting horror stories feature backups that "existed" right up until they were needed.

Mapping 3-2-1 onto an actual homelab

Copy one is the live data. Copy two is a snapshot repository on hardware that shares no failure mode with copy one — a USB disk, a second machine, a NAS; a second folder on the same SSD does not count. Copy three is off-site, because the failure modes that matter at home (theft, fire, a power event that takes every plugged-in disk, ransomware on the LAN) are exactly the ones that kill both local copies together. Scope it deliberately: back up app data, databases, photos, documents, and your compose files; skip media you can re-rip and container images you can re-pull. Most homelabs have 50–500GB of genuinely irreplaceable data, and pruning scope is what keeps copy three cheap.

Restic, Borg, or Duplicati

resticBorgBackupDuplicati
DeduplicationContent-defined chunksContent-defined chunksBlock-based
EncryptionAlways on, AES-256Optional, on by default in practiceAES-256
BackendsLocal, SFTP, S3/B2 and rclone (any cloud)Local, SSH (needs Borg on remote)Very broad
InterfaceCLICLI (borgmatic for config)Web GUI
Restore ergonomicsrestic restore / mountFUSE mount is excellentGUI wizard

restic and BorgBackup are both correct choices, and the real differentiator is backends: restic talks to object storage natively, while Borg wants SSH with Borg installed on the far end (rent that as a Hetzner Storage Box or BorgBase). Duplicati's GUI is appealing and version 2.1 stabilised a historically fragile local database, but it's still the one of the three I've seen fail restores most often in community reports — my opinion: fine for a third copy of non-critical data, not for the only backup of your photos. Pick restic if you're starting fresh.

A restic setup you can copy tonight

#!/bin/sh
export RESTIC_REPOSITORY=/mnt/backup/restic
export RESTIC_PASSWORD_FILE=/root/.restic-pass

restic backup /opt/stacks /srv/data --exclude '**/cache' --exclude '**/*.tmp'
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check --read-data-subset=5%
curl -fsS https://hc-ping.com/your-uuid-here

Run it from cron or a systemd timer at 03:00. The forget policy keeps 17 snapshots spanning six months while dedup keeps growth modest — my 180GB dataset holds six months of nightly history in about 210GB. The check --read-data-subset=5% line re-reads a rotating 5% of the repository every night, so silent corruption surfaces within weeks instead of at restore time. The final line is a dead-man switch: if the job stops running, the silence itself alerts you — the pattern is detailed in the monitoring guide. For the off-site copy, either run a second restic backup against a B2 repository or use restic copy to replicate snapshots; two independent targets protect against a corrupted repo being faithfully mirrored.

Databases: dump, don't copy

Copying a running database's files gives you a snapshot torn across writes — it often restores, until the night it doesn't. Dump first, then back up the dump:

docker exec postgres pg_dumpall -U postgres | gzip > /srv/dumps/pg-$(date +%F).sql.gz

The same applies to MariaDB (mariadb-dump --all-databases) and to SQLite (sqlite3 app.db ".backup ..."). Wire dumps to run just before the restic job. The lazy alternative — stopping the stack for the sixty seconds the backup takes — is genuinely fine at home and eliminates the whole class of consistency bugs.

Off-site options, priced

Backblaze B2 at $6/TB/month with free egress up to 3× your stored volume is the default and works with restic out of the box. A Hetzner Storage Box (1TB for about €4/month) speaks both SSH-for-Borg and SFTP-for-restic and is the better deal at the 1TB tier. The community classic costs nothing: a Raspberry Pi and USB disk at a friend's or parent's house, reachable over Tailscale, each of you hosting the other's copy three. Whatever the target, enable the client-side encryption both tools provide by default — off-site means someone else's premises.

The restore drill

Quarterly, twenty minutes: restore three random files from three different months and open them; restore one full application stack (compose file plus data) to a scratch directory and actually start it; time the process and write the timing down. The drill catches the classics — a password file that exists only on the machine that died (keep a copy in your password manager), an excluded directory that turned out to matter, a repo that prunes fine but restores at 2MB/s over your uplink. If the full-restore math says four days, you'll want to know that before the fire, not after.

What I'd do

restic to a USB disk on the server, nightly at 03:00 with the script above; restic copy to B2 or a Hetzner box right after; database dumps wired in before the snapshot; healthchecks pinging on success; a quarterly fifteen-minute restore drill on the calendar. Borg plus BorgBase is an equally sound stack if SSH targets suit you better — more tools live in the backup category. The tool matters far less than the drill: every backup system works in the demo, and only the tested one works in the disaster.