Skip to content
Operations

Off-site backups with Restic

Encrypted, deduplicated backups to a storage VPS in a different jurisdiction, in about twenty minutes — including the restore test almost everybody skips.

9 min readUpdated

RAID-10 survives a drive failure. It does not survive rm -rf, a bad migration, ransomware, or a legal event. This sets up encrypted, deduplicated backups to a second server in a different jurisdiction — which is what an off-site backup should mean.

The target

A Storage VPS in a country other than the one holding your primary server. If the primary is in Romania, put the backup in Iceland or Panama. Jurisdictional separation is the part most backup strategies leave out, and it is free to get right at setup time.

Install and initialise

# On the machine being backed up
apt -y install restic

# Generate a strong repository password and STORE IT SOMEWHERE ELSE
openssl rand -base64 32 > /root/.restic-pass
chmod 600 /root/.restic-pass

export RESTIC_REPOSITORY="sftp:backup@STORAGE_VPS_IP:/srv/backups/node-01"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
restic init
Lose the repository password and the backup is gone. Restic encrypts client-side, which is exactly what you want and exactly why there is no recovery path. Store it in a password manager, on paper, anywhere that is not the machine being backed up.

Back up

restic backup /etc /home /var/www /srv \
  --exclude-caches \
  --exclude '/var/www/*/cache' \
  --tag daily

The first run uploads everything. Subsequent runs upload only changed blocks, so a daily backup of a 90 GB server typically transfers a few hundred megabytes.

Automate it

# /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic backup
After=network-online.target

[Service]
Type=oneshot
Environment=RESTIC_REPOSITORY=sftp:backup@STORAGE_VPS_IP:/srv/backups/node-01
Environment=RESTIC_PASSWORD_FILE=/root/.restic-pass
ExecStart=/usr/bin/restic backup /etc /home /var/www /srv --exclude-caches --tag daily
ExecStartPost=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Daily restic backup

[Timer]
OnCalendar=*-*-* 03:17:00
RandomizedDelaySec=1800
Persistent=true

[Install]
WantedBy=timers.target
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer

The odd minute and the randomised delay stop every server you own from hitting the backup target simultaneously.

Verify — this is the step people skip

restic snapshots                          # list what exists
restic check --read-data-subset=5%        # verify integrity of a sample
restic restore latest --target /tmp/test --include /etc/nginx

Restore something, for real, once a quarter. An untested backup is a hypothesis. Put the restore test in your calendar, not in your intentions.

Database dumps

Backing up live database files produces a corrupt copy. Dump first:

# /etc/systemd/system/restic-backup.service — before ExecStart
ExecStartPre=/bin/sh -c 'pg_dumpall -U postgres | zstd > /srv/dumps/pg.sql.zst'
# or
ExecStartPre=/bin/sh -c 'mysqldump --all-databases --single-transaction | zstd > /srv/dumps/mysql.sql.zst'

Deploy in the next five minutes.

Pick a location, size the box, pay in crypto. No account signup wall, no ID, no waiting on a human.

7-day money-back guarantee · No KYC · Cancel any time from the panel