The question isn't if you'll need a backup. It's when. A proper backup strategy is the difference between a 5-minute recovery and a catastrophic data loss.
The 3-2-1 Rule
| Rule | Meaning | Example | |------|---------|---------| | 3 copies | Keep three copies of data | Original + 2 backups | | 2 media types | Store on two different media | VPS disk + object storage | | 1 off-site | At least one copy off-site | Remote server or cloud |
What to Back Up
| Data | Priority | Frequency | |------|----------|-----------| | Databases | Critical | Every 6 hours | | Application code | High | Daily (or use Git) | | Configuration files | High | After every change | | User uploads | Critical | Daily | | SSL certificates | Medium | After renewal | | Cron jobs and scripts | Medium | After changes |
Don't Back Up
- OS files (reinstall from scratch faster than restoring)
- Package cache (/var/cache/apt/)
- Temporary files (/tmp/)
- Log files (unless needed for compliance)
Automated Backup Script
#!/bin/bash
# /opt/backups/backup.sh
set -e
DATE=$(date +%Y%m%d-%H%M)
BACKUP_DIR="/opt/backups/local"
RETENTION_DAYS=7
mkdir -p "$BACKUP_DIR"
echo "Starting backup: $DATE"
# Database backup
echo "Backing up databases..."
mysqldump --all-databases --single-transaction > "$BACKUP_DIR/databases-$DATE.sql"
# Web files backup
echo "Backing up web files..."
tar czf "$BACKUP_DIR/www-$DATE.tar.gz" --exclude='node_modules' --exclude='.git' /var/www/
# Configuration backup
echo "Backing up configurations..."
tar czf "$BACKUP_DIR/config-$DATE.tar.gz" /etc/nginx/ /etc/php/ /etc/mysql/ /etc/letsencrypt/ /etc/crontab
# Remove old backups
echo "Cleaning old backups..."
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "*.sql" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: $DATE"
Schedule it:
crontab -e
# Run daily at 2 AM
0 2 * * * /opt/backups/backup.sh >> /var/log/backup.log 2>&1
Off-Site Backup with rclone
# Install rclone
sudo apt install rclone
rclone config # Set up your remote storage
Sync to Cloud Storage
# Sync local backups to cloud
rclone sync /opt/backups/local remote:vps-backups/
# Add to backup script
rclone copy "$BACKUP_DIR/databases-$DATE.sql" remote:vps-backups/
rclone copy "$BACKUP_DIR/www-$DATE.tar.gz" remote:vps-backups/
Supported Destinations
| Storage | Cost | Integration | |---------|------|-------------| | Backblaze B2 | $0.005/GB/month | rclone, s3cmd | | AWS S3 | $0.023/GB/month | rclone, aws-cli | | Wasabi | $0.0069/GB/month | rclone, s3cmd | | Google Cloud Storage | $0.020/GB/month | rclone | | Another VPS (rsync) | VPS cost | rsync |
Backup Verification
A backup that doesn't restore is not a backup:
#!/bin/bash
# /opt/backups/verify.sh
# Test database restore to temporary database
mysql -e "CREATE DATABASE backup_test;"
mysql backup_test < /opt/backups/local/databases-latest.sql
TABLES=$(mysql -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='backup_test';")
echo "Restored $TABLES tables"
mysql -e "DROP DATABASE backup_test;"
# Test file archive integrity
tar tzf /opt/backups/local/www-latest.tar.gz > /dev/null
echo "Archive integrity: OK"
Backup Schedule Recommendation
| Data Type | Frequency | Retention | Location | |-----------|-----------|-----------|----------| | Database | Every 6 hours | 14 days | Local + off-site | | Web files | Daily | 7 days | Local + off-site | | Configs | After change | 30 days | Local + off-site | | Full server | Weekly | 4 weeks | Off-site only |
Recovery Time Estimates
| Scenario | Recovery From | Estimated Time | |----------|-------------|----------------| | Deleted file | File backup | 5-10 minutes | | Corrupted database | Database dump | 10-30 minutes | | Hacked server | Full backup + clean reinstall | 1-3 hours | | Hardware failure | Off-site backup + new VPS | 2-4 hours |
Regular backups on Space-Node's VPS run fast thanks to NVMe SSD storage. Database dumps and file compressions complete quickly, minimizing the performance impact of backup operations.
