IAnswerable

Automated Server Backups with restic

27 Apr 2026

pg_dump piped to a bucket works until you have more than a database to protect — uploads, config, a Docker volume with state in it. restic backs up any directory, encrypts it client-side, deduplicates across runs, and can push to almost any storage you already have. It’s the tool to reach for once your backup script has grown past one file.

1. Install and initialize a repository

apt install restic   # or: brew install restic

Point it at an S3-compatible bucket (this works for MinIO, Backblaze B2, R2, or AWS):

export RESTIC_REPOSITORY=s3:https://s3.us-west-002.backblazeb2.com/my-backups
export RESTIC_PASSWORD=a-long-passphrase-you-will-not-lose
export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret

restic init

RESTIC_PASSWORD encrypts the repo. Write it down somewhere that isn’t this server — if you lose it, the backups are cryptographically unrecoverable, which is the point, but only if you remember that before you need a restore.

2. Back up something

restic backup /var/www /etc/nginx /home/app/uploads

For a database, dump to disk first, then back up the dump alongside everything else — restic doesn’t understand database internals, it just archives files:

pg_dump mydb > /backups/db.sql
restic backup /backups /var/www --exclude-caches

Every run after the first is fast. restic chunks files and only uploads new or changed chunks, so a nightly backup of a mostly-static app uploads megabytes, not gigabytes.

3. Set a retention policy and prune

Without this your bucket grows forever. Keep a sane window:

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

This keeps 7 daily snapshots, 4 weekly, 6 monthly, and deletes the data no longer referenced by any kept snapshot. --prune is what actually frees storage — forget alone just removes the snapshot pointer.

4. Cron the whole thing

# /etc/cron.d/restic-backup
0 3 * * * root /usr/local/bin/restic-backup.sh >> /var/log/restic.log 2>&1
#!/bin/bash
set -euo pipefail
pg_dump mydb > /backups/db.sql
restic backup /backups /var/www /etc/nginx --exclude-caches
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

set -euo pipefail matters here — without it a failed pg_dump still lets the script continue and back up a stale or empty dump file, and your cron log shows success.

Restoring

restic snapshots                          # list what you have
restic restore latest --target /tmp/restore

You can restore a single file too, without pulling the whole snapshot:

restic restore latest --target /tmp/restore --include /etc/nginx/nginx.conf

The failure mode to watch for

restic will happily back up a corrupted or empty file forever if nothing checks for it — deduplication means a broken dump that never changes uploads once and then costs nothing, which hides the problem instead of surfacing it. Run restic check monthly to verify repository integrity, and actually restore a snapshot to a scratch box quarterly. A backup tool that’s never been tested in reverse is a false sense of security with extra steps.