Professional Rsync Backup Strategy
A well-designed backup strategy is the difference between "we lost everything" and "we were back online in 20 minutes." This guide presents a production-tested approach to building a layered rsync backup system for any Linux server.
This strategy is application-centric — it focuses on backing up your apps, databases, and configs rather than full OS imaging. It works for any server stack: PHP/MySQL, Python/PostgreSQL, Node.js, static sites, Docker containers, or any combination.
Strategy Goals
| Goal | Description |
|---|---|
| Protect essential assets | Back up application files, databases, and configs first |
| Speed up recovery | Keep local restore points for fast rollback (minutes, not hours) |
| Add resilience | Maintain encrypted offsite copies for disaster recovery |
| Keep scope simple | Focus on application data, not full OS imaging |
| Automate everything | Remove human error from the backup process |
| Verify regularly | Test restores — a backup you can't restore is worthless |
The 3-2-1 Backup Rule
The industry-standard approach that this strategy implements:
3 copies of your data
2 different storage types (local disk + remote/cloud)
1 copy offsite (different server, region, or provider)
| Copy | Location | Purpose | Recovery Speed |
|---|---|---|---|
| Production | /var/www/, /var/lib/mysql/ | Live data | N/A |
| Local backup | /backup/ on same VPS or attached storage | Fast rollback | Minutes |
| Offsite backup | Remote server, S3, B2, Wasabi | Disaster recovery | Minutes to hours |
Core Backup Layers
Layer 1: Application Files
Your web applications, static files, and user-uploaded content.
| Content | Example Path | Sync Frequency |
|---|---|---|
| Web application code | /var/www/html/, /var/www/app/ | Daily |
| User uploads / media | /var/www/html/uploads/ | Hourly or daily |
| Configuration files | /etc/nginx/, /etc/php/ | On change |
| Docker data | /opt/docker-data/ | Daily |
# Daily application backup
rsync -avz --delete \
--exclude 'cache/' \
--exclude '*.log' \
--exclude 'node_modules/' \
/var/www/html/ /backup/daily/www/
Layer 2: Database Backups
Export databases first, then rsync the dumps. Never rsync live database files directly.
# MySQL / MariaDB
mysqldump -u root -p --all-databases | gzip > /var/backups/db/all_databases_$(date +%F).sql.gz
# PostgreSQL
pg_dumpall -U postgres | gzip > /var/backups/db/all_databases_$(date +%F).sql.gz
# Sync database dumps to backup location
rsync -avz /var/backups/db/ /backup/daily/db/
Never rsync live database data directories (/var/lib/mysql/, /var/lib/postgresql/). The files change constantly during operation and you'll get a corrupt backup. Always use mysqldump or pg_dump first, then rsync the exported files.
Layer 3: Offsite Replication
Push local backups to a remote server or cloud storage for disaster recovery:
# Rsync to a remote backup server
rsync -avz --delete /backup/daily/ user@backup-server:/offsite/daily/
# Alternative: Use rclone for cloud storage (S3, B2, Wasabi)
rclone sync /backup/daily/ remote:my-bucket/daily/
Retention Policy
Not every backup needs to be kept forever. A Grandfather-Father-Son (GFS) retention policy gives you multiple restore points while managing storage:
| Tier | Keep | Count | Use Case |
|---|---|---|---|
| Hourly | Last 24 hours | 24 copies | Quick rollback for frequent changes |
| Daily | Last 7 days | 7 copies | Standard restore points |
| Weekly | Last 4 weeks | 4 copies | Historical recovery |
| Monthly | Last 3–12 months | 3–12 copies | Compliance, auditing |
| Offsite | Latest daily + weekly | 2+ copies | Disaster recovery |
Implementing Retention with Rsync
#!/bin/bash
# retention-rotate.sh — Rotate daily backups into weekly/monthly
BACKUP_BASE="/backup"
TODAY=$(date +%F)
DAY_OF_WEEK=$(date +%u) # 1=Monday, 7=Sunday
DAY_OF_MONTH=$(date +%d)
# Daily backup (always runs)
rsync -avz --delete /var/www/html/ "$BACKUP_BASE/daily/$TODAY/"
# Weekly snapshot (every Sunday)
if [ "$DAY_OF_WEEK" -eq 7 ]; then
cp -al "$BACKUP_BASE/daily/$TODAY/" "$BACKUP_BASE/weekly/week-$(date +%V)/"
fi
# Monthly snapshot (1st of each month)
if [ "$DAY_OF_MONTH" -eq "01" ]; then
cp -al "$BACKUP_BASE/daily/$TODAY/" "$BACKUP_BASE/monthly/$(date +%Y-%m)/"
fi
# Clean up old dailies (keep last 7 days)
find "$BACKUP_BASE/daily/" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
# Clean up old weeklies (keep last 4 weeks)
find "$BACKUP_BASE/weekly/" -maxdepth 1 -type d -mtime +28 -exec rm -rf {} \;
The cp -al command creates hard-linked copies — they share disk blocks with the original, so weekly/monthly snapshots use almost zero extra disk space until files change.
Reference Architecture
flowchart TD
APP["Application Files<br/>/var/www/html/"] --> LFB["Local File Backup<br/>/backup/daily/www/"]
DB["Database Export<br/>mysqldump / pg_dump"] --> LDB["Local DB Backup<br/>/backup/daily/db/"]
CFG["Config Files<br/>/etc/nginx/, .env"] --> LCFG["Local Config Backup<br/>/backup/daily/config/"]
LFB --> RET["Retention Rotation<br/>Daily → Weekly → Monthly"]
LDB --> RET
LCFG --> RET
RET --> OFF["Offsite Replication<br/>rsync or rclone to remote server/cloud"]
OFF --> VER["Integrity Verification<br/>Checksum comparison"]
VER --> TEST["Restore Test<br/>Periodic restore to staging"]
Professional Patterns
| Pattern | Description | Best For | Trade-off |
|---|---|---|---|
| Rsync incremental + DB dump | Sync changed files, export databases regularly | Most VPS setups | Needs local disk planning |
| Hard-linked snapshots | rsync --link-dest for space-efficient versioning | Budget-conscious servers | Slightly complex setup |
| Encrypted repository | Use restic or borg for compression + encryption | Compliance requirements | More tooling complexity |
| Hybrid GFS retention | Hourly → daily → weekly tiers | High-change applications | More policy management |
| Cloud mirror | Push backups to S3/B2/Wasabi | Maximum durability | Slower restores |
Operational Checklist
| Area | Expected Practice | |
|---|---|---|
| Scheduling | Automated via cron or systemd timer — no manual runs | |
| Database exports | mysqldump / pg_dump runs before file sync | |
| Encryption | Offsite backups encrypted at rest (GPG, rclone crypt) | |
| Verification | Checksum or file count checks after each backup | |
| Monitoring | Alerts on backup failure (email, Slack, webhook) | |
| Restore testing | Restore to staging at least monthly | |
| Documentation | Backup paths, credentials, and procedures documented |
Quick-Start: Minimal Production Backup
For a single VPS running a PHP/MySQL application, here's the minimum viable backup:
#!/bin/bash
# minimal-backup.sh — Run daily via cron
set -e
TIMESTAMP=$(date +%F_%H%M)
BACKUP_DIR="/backup/$TIMESTAMP"
REMOTE="user@backup-server:/offsite"
# 1. Export database
mkdir -p "$BACKUP_DIR/db"
mysqldump -u root -p'yourpassword' --all-databases \
| gzip > "$BACKUP_DIR/db/all_databases.sql.gz"
# 2. Sync application files
rsync -az --delete \
--exclude 'cache/' \
--exclude '*.log' \
/var/www/html/ "$BACKUP_DIR/www/"
# 3. Copy configs
rsync -a /etc/nginx/ "$BACKUP_DIR/config/nginx/"
cp /var/www/html/.env "$BACKUP_DIR/config/env_backup"
# 4. Push to offsite
rsync -avz "$BACKUP_DIR/" "$REMOTE/$TIMESTAMP/"
# 5. Clean local backups older than 7 days
find /backup/ -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
echo " Backup completed: $TIMESTAMP"
Add to cron for daily execution:
# Run at 2:00 AM daily
0 2 * * * /usr/local/bin/minimal-backup.sh >> /var/log/backup.log 2>&1
Common Pitfalls
| Pitfall | Impact | Prevention |
|---|---|---|
| Backing up live database files | Corrupt, unusable backup | Always use mysqldump / pg_dump first |
| No offsite copy | Single point of failure | Replicate to remote server or cloud |
| Never testing restores | Discover backup is broken only during disaster | Schedule monthly restore tests |
| No monitoring or alerts | Backup silently fails for weeks | Add success/failure notifications |
| Storing passwords in scripts | Security risk | Use .my.cnf or environment variables |
| No retention policy | Disk fills up or oldest backups lost | Implement GFS rotation |
What's Next
Continue building your rsync knowledge:
- Source and Destination — Master path rules for reliable syncs
- Cron Automation — Set up automated backup schedules
- Backup Strategies — Deep dive into advanced backup architectures
- Disaster Recovery — Plan for worst-case scenarios