Why restic (and where Borg fits)
restic and Borg are the two deduplicating, encrypted, snapshot-based backup tools worth your time. Both store data in chunks so a nightly run only uploads what changed, both encrypt client-side so the storage host never sees plaintext, and both do point-in-time snapshots you can browse.
The practical difference: restic talks natively to object storage (S3, Backblaze B2, plain SFTP, and more) with a single static binary, which makes offsite trivial. Borg is a touch faster and more mature but wants a Borg-aware destination — usually another box you control or a hosting service that speaks the protocol. If your offsite target is cheap object storage, use restic. If it's a second server you own, Borg is excellent. I'll show restic; the concepts map directly.
Set up the repository
Install restic (it's a single binary; the distro package is usually fine) and point it at a bucket. For Backblaze B2 as an example:
export RESTIC_REPOSITORY="b2:my-bucket:vps-backups"
export B2_ACCOUNT_ID="your-key-id"
export B2_ACCOUNT_KEY="your-application-key"
export RESTIC_PASSWORD="a-long-random-passphrase-you-will-store-safely"
restic initStop and read this next sentence twice. That RESTIC_PASSWORD is the encryption key. There is no recovery, no reset, no support ticket that brings it back. Lose it and your backups are cryptographic noise, forever. Put it in a password manager and, honestly, on paper somewhere too. More backups have been lost to a forgotten passphrase than to any disk failure.
What to actually back up
Don't image the whole filesystem. You don't need /usr or /var/cache — you need your data and your config. A focused backup restores faster and dedupes better:
restic backup /etc /home /srv /opt/app/configDatabases are the classic trap. Do not back up the live data directory of Postgres or MySQL by copying files — you'll capture a snapshot mid-transaction and get an archive that restores into a corrupt database. Dump first, then back up the dump:
pg_dump -Fc mydb > /srv/dumps/mydb.dumpRun the dump in the same script, right before the restic call. Same goes for anything holding an open write handle: containers with volumes, SQLite files, Redis. Get a consistent export, then archive the export.
Automate it, then verify the automation
Wrap the exports and the backup in one script and run it from a systemd timer or cron. Add retention so the repo doesn't grow forever:
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --pruneNow the step people skip: prove the cron job is actually running and succeeding. A backup script that fails silently is worse than none, because it manufactures false confidence. Have the script exit non-zero on failure and pipe that into something that pings you — a healthcheck URL, an email, anything that makes silence audible. If you only notice backups when they fail loudly, you'll never notice they stopped.
The restore drill
This is the whole point of the article. Schedule an actual restore, on purpose, to a scratch directory, before you need it.
List your snapshots:
restic snapshotsRestore the most recent one somewhere harmless:
restic restore latest --target /tmp/restore-testThen look at what came out. Not "did the command exit 0" — actually inspect it. Is the database dump there and non-empty? Load it into a throwaway database and confirm it imports cleanly:
pg_restore --list /tmp/restore-test/srv/dumps/mydb.dumpOpen a config file. Check a few user files are the right size and not zero bytes. The first time I did this on a "working" backup, I discovered the dump was 40 bytes — the pg_dump had been failing because the cron environment didn't have the DB password, and the empty output backed up perfectly every night. Green checkmarks the whole way. The restore drill is the only thing that catches that.
Do this drill when you set up backups, and then again every few months, because the thing that breaks a backup is usually a change you made to the server after you set it up.
A few gotchas that bite people
- The forgotten passphrase, covered above, is the number-one way people lose restic/Borg repos. Store it before you store anything else.
- Backing up to the same provider as the server. If your VPS host has a bad day and your backups are in the same account, you can lose both. Offsite means a different vendor, ideally a different login.
- Retention that quietly deletes your only good copy. If you keep 7 daily snapshots and don't notice corruption for two weeks, every clean snapshot has aged out. Keep some monthlies for exactly this reason.
- Backing up secrets in plaintext. Your
/etcmay contain API keys and TLS private keys. That's fine inside an encrypted restic repo — it is emphatically not fine if you everrestic restoreto a shared or public directory. Mind where the plaintext lands.
Backups are cheap insurance until the day they're the only thing between you and starting over. On a small VPS — the kind you can spin up on NoctHost with an email and a crypto top-up — a second cheap box or a few dollars of object storage is all the offsite you need. What you can't buy after the fact is a restore you tested before the disk died.