Blog / Tutorials

VPS Backups That Actually Restore — restic, Borg, and a Real Drill

By the NoctHost TeamAugust 14, 20266 min read

Here is the uncomfortable truth about backups: most of them don't work, and the owner finds out at the worst possible moment. The backup ran green every night for a year, and then the day the volume died it turned out the cron job had been silently failing since a disk-full event in March, or the archive restored fine but the database inside it was a corrupt mid-write snapshot.

An untested backup is not a backup. It's a hope. So this covers two things: setting up encrypted, offsite backups with restic, and — the part nobody does — actually restoring them to prove they work.

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 init

Stop 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/config

Databases 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.dump

Run 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 --prune

Now 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 snapshots

Restore the most recent one somewhere harmless:

restic restore latest --target /tmp/restore-test

Then 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.dump

Open 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.

Tip — Restore to a different machine at least once. Restoring on the same box can accidentally rely on state that lives there — a config file, an env var, a running service. A cold restore onto a fresh VPS is the honest test of "could I actually rebuild from this."

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 /etc may contain API keys and TLS private keys. That's fine inside an encrypted restic repo — it is emphatically not fine if you ever restic restore to 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.

Spin one up in about a minute

Email signup, pay with crypto, hourly billing. Trying a box costs cents — destroy it when you are done.

Deploy a server

Frequently asked

restic or Borg — which should I use?
Use restic if your backup target is object storage (S3, B2, Wasabi) or plain SFTP; its native cloud support makes offsite effortless. Use Borg if you're backing up to another server you control and want slightly better speed and maturity. They're both excellent; the destination decides.
How often should I test a restore?
At setup, and then every few months or after any significant change to what you're backing up. The failures that matter almost always come from a config drift after the initial setup, so a one-time test isn't enough.
Do I need to stop my database to back it up?
No — but you must dump it rather than copy its live files. Run pg_dump (or mysqldump) to get a consistent export, then back up that file. Copying the raw data directory of a running database gives you a snapshot that may not restore.
Is client-side encryption enough for untrusted storage?
Yes. Both restic and Borg encrypt before anything leaves your server, so the storage provider only ever sees ciphertext. That's what makes cheap public object storage a safe backup target — provided you never lose the passphrase, which is the one secret they can't help you recover.

Keep reading