Setting Up Automated Linux Server Backups with Restic
A 3-2-1 backup strategy for production servers: encrypted, deduplicated, restorable on demand — using Restic + Cloudflare R2.
The 3-2-1 rule
For production data:
- 3 copies (production + 2 backups)
- 2 different media (local disk + cloud)
- 1 offsite (outside the same data center)
What we use for hosting clients: Restic (open source) + an S3-compatible object store.
Why Restic, not rsync or tar
- Encrypted by default — AES-256 + password-protected repository. Safe even on a public S3 bucket.
- Deduplicated — block-level dedup. Daily incremental backups only store deltas. 30 dailies often save 90%+ space.
- Cross-platform — back up from a Linux server, restore on a dev laptop if needed.
- Mountable snapshots —
restic mountexposes old snapshots as a read-only filesystem. Need a file from two weeks ago?cd /mnt/restic/snapshots/2025-09-15/.
Standard setup (what we use)
# Init repo to Cloudflare R2 (S3-compatible)
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export RESTIC_REPOSITORY="s3:https://r2.lancartech.dev/backups/myapp"
export RESTIC_PASSWORD_FILE="/root/.restic-pass"
restic init
# Backup script
restic backup /var/www /etc /opt/myapp \
--exclude '*.log' \
--exclude 'node_modules' \
--tag daily
# Retention
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
Cron daily at 02:00 + lock file to prevent overlap.
Commonly missed
- Test restores periodically — a backup you’ve never restored isn’t a backup. Once a month we spin a blank VM and restore a full snapshot to it.
- Dump the database first, then back up — backing up mysqldata directly without stopping the daemon can corrupt the snapshot. Use
mysqldumporpg_dumpfor consistent dumps. - Monitor backup success — don’t assume cron runs. Use a healthcheck endpoint (Healthchecks.io or similar) that pings WhatsApp on failure.
Disaster-recovery consult if you’re rethinking your backup strategy.