Website Regular Backup
Backup is insurance that costs little until you need it. The cost of storing 10 GB on S3 is about $0.23/month. The cost of losing data without a backup is unpredictable.
The 3-2-1 Rule
- 3 copies of data
- 2 different storage media/repositories
- 1 copy offsite (different location or cloud)
What to Back Up
- Database (updated most frequently — critically important)
- User-uploaded files (uploads/, storage/)
- Configuration files (.env, nginx.conf)
- Site code (usually already in Git — additional backup not needed)
Automatic DB Backup to S3
#!/bin/bash
# /opt/scripts/backup-db.sh
set -euo pipefail
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="mysite_prod"
BACKUP_DIR="/tmp/backups"
S3_BUCKET="s3://my-backups/database"
mkdir -p "$BACKUP_DIR"
# PostgreSQL
pg_dump -U postgres -d "$DB_NAME" -F custom \
| gzip > "$BACKUP_DIR/${DB_NAME}_${DATE}.dump.gz"
# Upload to S3
aws s3 cp "$BACKUP_DIR/${DB_NAME}_${DATE}.dump.gz" \
"${S3_BUCKET}/${DB_NAME}_${DATE}.dump.gz" \
--storage-class STANDARD_IA
# Delete local file
rm "$BACKUP_DIR/${DB_NAME}_${DATE}.dump.gz"
# Delete backups older than 30 days
aws s3 ls "${S3_BUCKET}/" \
| awk '{print $4}' \
| while read key; do
date=$(echo "$key" | grep -oP '\d{8}')
if [[ $(date -d "$date" +%s) -lt $(date -d "30 days ago" +%s) ]]; then
aws s3 rm "${S3_BUCKET}/${key}"
fi
done
echo "Backup completed: ${DB_NAME}_${DATE}.dump.gz"
# Crontab: backup every 6 hours
0 */6 * * * /opt/scripts/backup-db.sh >> /var/log/backup.log 2>&1
S3 Lifecycle Policies
{
"Rules": [{
"ID": "BackupRetention",
"Filter": { "Prefix": "database/" },
"Status": "Enabled",
"Transitions": [
{ "Days": 7, "StorageClass": "STANDARD_IA" },
{ "Days": 30, "StorageClass": "GLACIER" }
],
"Expiration": { "Days": 90 }
}]
}
File Backup (rsync + S3)
# Backup uploads/ to S3
aws s3 sync /var/www/mysite/storage/app/public/ \
s3://my-backups/files/ \
--storage-class STANDARD_IA \
--delete # delete in S3 what was deleted locally
# Without --delete (safer, but volume grows)
aws s3 sync /var/www/mysite/uploads/ s3://my-backups/uploads/
Backup Verification (Mandatory!)
Backup without restore verification is false confidence:
# Monthly restore verification
aws s3 cp s3://my-backups/database/latest.dump.gz /tmp/test-restore.dump.gz
createdb mysite_restore_test
gunzip < /tmp/test-restore.dump.gz | pg_restore -d mysite_restore_test
psql -d mysite_restore_test -c "SELECT COUNT(*) FROM users;" # should match production
dropdb mysite_restore_test
Solutions for WordPress
# WP-CLI + S3
wp package install wp-cli/db-command --allow-root
wp db export - | gzip | aws s3 cp - s3://my-backups/wp_$(date +%Y%m%d).sql.gz
Setting up automatic backup with S3 storage — 2–4 hours.







