The uncomfortable definition of a backup
Here is the sentence that should be printed above every backup script: a backup you have never restored is a hypothesis, not a backup. A cron job that copies files somewhere is easy. A repository you can decrypt, read, and restore into a working system on the worst day of your year is a different thing entirely, and the gap between them is exactly where people discover — too late — that the archive was empty, the passphrase was lost, or the “offsite” copy sat on the same disk that just died.
So this piece is not about installing a backup tool. It is about setting one up in the shape that survives contact with a real incident: encrypted before it leaves the machine, scheduled so nobody has to remember it, and proven by an actual restore. restic and Borg are both excellent at this; the commands are trivial, and the discipline around them is the entire job.
Before the first package: read what you are protecting
A backup that points at the wrong directory, or duplicates a repository that already exists, is worse than none — it looks like protection while protecting nothing. Reconnaissance is the difference.
- Is the tool already here?
command -v restic && restic version, orcommand -v borg && borg --version. If it is, you may already have a repository and a timer — audit them before you add a second, competing one. - Does the source actually exist?
test -d /var/www && echo ok || echo missing. A backup of a path that is not there is a green checkmark over nothing. Confirm the directory before you schedule anything against it. - Where is the repository, and is it off this machine? A local repository on the same disk as your data protects you from
rm -rf, not from a disk failure. If the plan is S3 or a remote host, verify you can reach it before the first run, not during the first outage.
1. Install the tool — and know what “client-side encryption” buys you
Installation is the least interesting step: apt-get update && apt-get install -y restic, or apt-get install -y borgbackup. The proof it worked is a version string from restic version or borg --version — not that apt exited zero.
What actually matters is a property both tools share: they encrypt on the machine, before anything is written to the repository. The bucket, the remote host, the external drive — none of them ever see your files in the clear. This is the same shape as a zero-knowledge system: the place that stores your data cannot read it. It means an S3 bucket that leaks, or a storage provider you do not fully trust, leaks ciphertext. It also means the encryption key is now something you are responsible for, which is the trade you are making and the risk covered below.
2. Initialise the repository, deliberately
With restic, restic init against the target — a local path, an S3 URL, an SFTP host — creates the encrypted repository. With Borg, the choice to make explicit is the encryption mode: borg init --encryption=repokey-blake2 keeps the key material in the repository, protected by a passphrase, which is the sane default for a server backing itself up.
The single most consequential decision in this whole exercise is where the repository password or Borg passphrase lives. It must never sit in a world-readable file, in shell history, or in a log line. Put it in a file readable only by the account that runs the backup (mode 600), keep a copy in a password manager off the machine, and understand the consequence in full: this secret is the only thing standing between a disk failure and total recovery — and also the only thing that, if lost, makes the data unrecoverable forever. That is not a bug. It is the price of client-side encryption, and it is worth paying with your eyes open.
The verification for this step is not “the command exited 0”. It is restic snapshots or borg info "$BORG_REPO" answering — a repository that reports its own state is a repository that exists and that your key can open.
3. Schedule with a systemd timer, and prune with intent
The scheduled run is a systemd timer, not a line buried in a crontab nobody reads. A timer is introspectable — systemctl list-timers shows you when it last ran and when it runs next — and that visibility is the point. Verify it exists with systemctl list-timers | grep -c restic (or borg), and you have proof the job is scheduled rather than a belief that it is.
Retention is where a backup either stays useful or quietly turns into an unbounded bill. Both tools prune old snapshots on a policy — keep so many daily, weekly, monthly — with restic forget --prune or borg prune. The rule that matters: a prune deletes archives, so it is the one part of the pipeline to treat with the same care as a delete. Never prune archives you have not confirmed are superseded, and never point a prune at a repository whose retention you have not read. A too-aggressive retention policy is a slow-motion data-loss incident that looks like housekeeping.
What a backup does not give you
A timer that runs is not a restore that works — the two are related only by hope until you prove otherwise. A repository is not a rollback: restic and Borg are for recovering data over minutes or hours, not for un-doing a bad deploy in seconds; that is what a snapshot before a risky change is for. Client-side encryption does not protect you from losing the key — lose the passphrase and the repository is noise, permanently, by design. And a single copy is not a backup strategy: the old 3-2-1 rule — three copies, two media, one offsite — exists because the failure that takes your data often takes anything sitting right next to it. Convenience is not recoverability.
The check that proves you can recover
In order: restic version or borg --version for the tool; a repository that answers restic snapshots / borg info so you know your key opens it; systemctl list-timers showing the timer scheduled and its last run succeeded; and — the one that actually matters — a real test restore into a temporary directory, compared against the source, so you have restored the data at least once before you needed to. A restore you have performed is the only verification a backup ever truly passes.
Where Servor fits
Servor ships restic and Borg backups as recipes — part of 150+ vetted playbooks — and the recipe shape is what keeps a “set it and forget it” task from being forgotten in the wrong way. A recipe is not a script fired blind: it starts with reconnaissance, reading whether the tool is installed and whether the source directory even exists, so it never schedules a backup of nothing. Each step carries its own verify — the version string, the repository that answers, the timer listed — so the run proves it worked. The playbook is idempotent: an existing repository is checked and reported, not re-initialised over, and it will not silently delete archives.
The guardrails are written in because the defaults are dangerous: the repository password is never placed in a world-readable file or a log, and a prune is treated as the destructive operation it is — proposed and explained, not slipped through. And the execution never leaves the zero-knowledge path: every command is signed in your browser with a key derived from your vault key, relayed verbatim, and verified by the agent on the machine before it runs. The agent reaches the control plane over an outbound connection, so a backup target on a locked-down network never needs an inbound hole.
For the database-specific angle — dumps, point-in-time recovery, the difference between a copy and a consistent snapshot — see the PostgreSQL in production piece. For the discipline that makes any risky change reversible, read the safe destructive operations piece, and the approve-sign-verify loop behind all of it is in the Plan-Execute-Verify piece. Browse the rest of the recipe catalogue, or see how it all fits in our server operations guide.