Skip to content
NimbusVPS
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. Choose a separate failure domain and review the target service price; catalogue locations use different multipliers.

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 transfer volume depends on what changed and on deduplication; measure your own dataset before choosing a transfer allowance.

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'

Choose your server and see the total.

Choose your location, resources and term. Pay in crypto after reviewing the total; payment is verified automatically before your VPS is prepared.

Minimum 3-month VPS term · No ID upload in checkout · 7-day refund policy with exclusions