| name | backup-recovery |
| description | Implement backup and recovery strategies. Configure rsync, Restic, and cloud backups. Use when designing data protection solutions. |
| license | MIT |
| metadata | {"author":"devops-skills","version":"1.0"} |
Backup and Recovery
Implement comprehensive backup and recovery strategies using rsync, Restic, and cloud storage backends. Covers the 3-2-1 rule, automated scheduling, S3/B2 backends, encryption, restore procedures, and verification testing.
When to Use
- Designing a backup strategy for servers, databases, or application data
- Setting up Restic for encrypted, deduplicated backups to local or cloud storage
- Automating backups with systemd timers or cron
- Restoring data after accidental deletion, corruption, or disaster
- Migrating data between environments using backup/restore workflows
- Verifying backup integrity and testing recovery procedures
Prerequisites
rsync installed (included in most Linux distributions)
restic installed (v0.16+ recommended)
- Cloud CLI configured for the backend: AWS CLI for S3,
b2 CLI for Backblaze B2
- Sufficient storage at the backup destination (2-3x source size for retention)
- SSH access for remote rsync targets
systemd or cron for scheduling
The 3-2-1 Backup Rule
- 3 copies of your data (1 primary + 2 backups)
- 2 different storage media or types (e.g., local disk + cloud)
- 1 copy offsite (cloud storage, remote datacenter)
rsync Backups
Basic Operations
rsync -avz --delete /data/ /backup/data/
rsync -avz -e "ssh -i ~/.ssh/backup_key" /data/ backup@remote:/backups/server01/
rsync -avz --delete \
--exclude='*.tmp' \
--exclude='*.log' \
--exclude='.cache/' \
--exclude='node_modules/' \
/data/ /backup/data/
rsync -avz --delete --exclude-from=/etc/backup-excludes.txt /data/ /backup/data/
rsync -avzn --delete /data/ /backup/data/
rsync -avz --bwlimit=50000 --progress /data/ backup@remote:/backups/
Incremental Backups with Hard Links
rsync -avz --delete \
--link-dest=/backup/daily/latest \
/data/ /backup/daily/$(date +%Y-%m-%d)/
ln -snf /backup/daily/$(date +%Y-%m-%d) /backup/daily/latest
find /backup/daily -maxdepth 1 -type d -name "20*" -mtime +30 -exec rm -rf {} \;
Restic Backup
Installation
apt install -y restic
dnf install -y restic
curl -L https://github.com/restic/restic/releases/latest/download/restic_0.17.3_linux_amd64.bz2 \
| bunzip2 > /usr/local/bin/restic
chmod +x /usr/local/bin/restic
restic version
Initialize a Repository
restic init --repo /backup/restic-repo
export AWS_ACCESS_KEY_ID="AKIAEXAMPLE"
export AWS_SECRET_ACCESS_KEY="secretkey"
restic init --repo s3:s3.amazonaws.com/my-backup-bucket
export AWS_ACCESS_KEY_ID="minioadmin"
export AWS_SECRET_ACCESS_KEY="miniosecret"
restic init --repo s3:http://minio.example.com:9000/backup-bucket
export B2_ACCOUNT_ID="accountid"
export B2_ACCOUNT_KEY="accountkey"
restic init --repo b2:my-backup-bucket:server01
restic init --repo sftp:backup@remote:/backups/server01
echo "my-secure-repo-password" > /etc/restic/password.txt
chmod 600 /etc/restic/password.txt
Backup Operations
restic backup /data --repo /backup/restic-repo --password-file /etc/restic/password.txt
restic backup /data /etc /var/lib/postgresql \
--repo s3:s3.amazonaws.com/my-backup-bucket \
--password-file /etc/restic/password.txt
restic backup /data \
--exclude='*.tmp' \
--exclude='*.log' \
--exclude-file=/etc/restic/excludes.txt \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
restic backup /data \
--tag server01 --tag production --tag daily \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
pg_dump -U postgres mydb | restic backup --stdin --stdin-filename mydb.sql \
--repo s3:s3.amazonaws.com/my-backup-bucket \
--password-file /etc/restic/password.txt
restic backup /data -v \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
Snapshot Management
restic snapshots --repo /backup/restic-repo --password-file /etc/restic/password.txt
restic ls latest --repo /backup/restic-repo --password-file /etc/restic/password.txt
restic diff abc123 def456 --repo /backup/restic-repo --password-file /etc/restic/password.txt
Retention Policy (forget + prune)
restic forget \
--keep-daily 7 --keep-weekly 4 --keep-monthly 12 --keep-yearly 3 \
--prune \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 \
--dry-run --repo /backup/restic-repo --password-file /etc/restic/password.txt
Restore Procedures
restic restore latest --target /restore \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
restic restore abc123 --target /restore \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
restic restore latest --target /restore --include "/data/config" \
--repo /backup/restic-repo \
--password-file /etc/restic/password.txt
mkdir -p /mnt/restic
restic mount /mnt/restic --repo /backup/restic-repo --password-file /etc/restic/password.txt &
Verification
restic check --repo /backup/restic-repo --password-file /etc/restic/password.txt
restic check --read-data --repo /backup/restic-repo --password-file /etc/restic/password.txt
restic check --read-data-subset=5% --repo /backup/restic-repo --password-file /etc/restic/password.txt
Automated Backup with Environment File
/etc/restic/env
export RESTIC_REPOSITORY="s3:s3.amazonaws.com/my-backup-bucket"
export RESTIC_PASSWORD_FILE="/etc/restic/password.txt"
export AWS_ACCESS_KEY_ID="AKIAEXAMPLE"
export AWS_SECRET_ACCESS_KEY="secretkey"
export RESTIC_CACHE_DIR="/var/cache/restic"
Backup Script
#!/bin/bash
set -euo pipefail
source /etc/restic/env
LOG="/var/log/restic-backup.log"
echo "$(date): Starting backup" >> "$LOG"
restic backup /data /etc /var/lib/postgresql \
--exclude-file=/etc/restic/excludes.txt \
--tag "$(hostname)" --tag daily \
--verbose >> "$LOG" 2>&1
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 \
--prune >> "$LOG" 2>&1
[ "$(date +%u)" -eq 7 ] && restic check --read-data-subset=10% >> "$LOG" 2>&1
echo "$(date): Backup completed" >> "$LOG"
chmod +x /usr/local/bin/restic-backup.sh
Scheduled Backups
Systemd Timer (Recommended)
[Unit]
Description=Restic backup
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/restic-backup.sh
Nice=10
IOSchedulingClass=idle
[Unit]
Description=Run Restic backup daily at 2 AM
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=900
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer
systemctl start restic-backup.service
journalctl -u restic-backup.service -f
Database Backups with Restic
pg_dump -U postgres -Fc mydb | restic backup --stdin --stdin-filename mydb.dump \
--tag postgres --tag mydb \
--repo s3:s3.amazonaws.com/my-backup-bucket \
--password-file /etc/restic/password.txt
mysqldump --all-databases --single-transaction | \
restic backup --stdin --stdin-filename all-databases.sql \
--tag mysql \
--repo s3:s3.amazonaws.com/my-backup-bucket \
--password-file /etc/restic/password.txt
restic dump latest mydb.dump \
--repo s3:s3.amazonaws.com/my-backup-bucket \
--password-file /etc/restic/password.txt \
| pg_restore -U postgres -d mydb --clean --if-exists
Troubleshooting
| Symptom | Diagnostic Command | Common Fix |
|---|
| "repository not initialized" | restic cat config --repo <repo> | Run restic init --repo <repo> first |
| "wrong password" | Check env vars / password file | Verify RESTIC_PASSWORD_FILE contents and permissions |
| Backup is slow | restic backup -v for progress | Check network bandwidth; exclude large unneeded dirs |
| S3 permission denied | aws s3 ls s3://bucket/ | Check IAM policy includes s3:GetObject, s3:PutObject |
| "unable to create lock" | restic unlock --repo <repo> | A previous backup crashed; unlock the repository |
| Restore shows empty dirs | restic ls <snapshot-id> | Verify correct snapshot ID; check --include path syntax |
| Repository growing too large | restic stats --repo <repo> | Run restic forget --prune with stricter retention |
| Check fails with pack errors | restic check --read-data | Rebuild index: restic rebuild-index; restore from another copy |
Related Skills
linux-administration -- Server maintenance and log management
systemd-services -- Scheduling backups with systemd timers
object-storage -- S3 and MinIO as backup destinations
block-storage -- LVM snapshots for consistent backups
nfs-storage -- Backing up NFS-shared data