RDB backup scheduling, AOF log shipping to cold storage, BGSAVE + BGREWRITEAOF interaction, DEBUG RELOAD for in-memory consistency checks, and a documented recovery runbook for the three most common failure scenarios.
A-13 — Disaster Recovery, Backup, and Point-in-Time Restore
Who this module is for: Redis is holding data that matters — sessions, job queues, rate limit state, coordination locks. If the server dies, you need to recover. This module covers RDB backup scheduling, AOF log shipping, recovery runbooks for the three most common failure scenarios, and the one practice most teams skip: testing restore procedures before they need them.
The Backup Strategy Pyramid
Not all Redis data needs the same protection:
| Data Type | Recovery Strategy | RTO | RPO |
|---|---|---|---|
| Pure cache | Regenerate from DB on miss | Seconds | Any (cache is disposable) |
| Session store | Restore from RDB + accept some logouts | Minutes | Last snapshot |
| Job queue | Restore from RDB + reprocess in-flight jobs | Minutes | Last snapshot |
| Rate limit state | Restore from RDB + accept burst on restart | Minutes | Last snapshot |
| Event log / audit trail | AOF log shipping + S3 | < 30 min | < 1 minute |
RPO (Recovery Point Objective): Maximum acceptable data loss (time).
RTO (Recovery Time Objective): Maximum acceptable downtime (time to recover).
Design your persistence configuration to meet the RPO; design your restore procedure to meet the RTO.
RDB Backup Scheduling
Automated Periodic Snapshots
#!/bin/bash
# /opt/scripts/redis-backup.sh — run via cron
REDIS_CLI="/usr/bin/redis-cli"
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
BACKUP_DIR="/backups/redis"
S3_BUCKET="s3://my-company-backups/redis"
DATE=$(date +%Y%m%d-%H%M)
# Trigger a background save
$REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT BGSAVE
# Wait for save to complete (poll rdb_bgsave_in_progress)
while [ $($REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT INFO persistence | grep rdb_bgsave_in_progress | cut -d: -f2 | tr -d '\r') -eq 1 ]; do
sleep 1
done
# Check if last save was successful
BGSAVE_STATUS=$($REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT INFO persistence | grep rdb_last_bgsave_status | cut -d: -f2 | tr -d '\r\n ')
if [ "$BGSAVE_STATUS" != "ok" ]; then
echo "BGSAVE failed!" >&2
exit 1
fi
# Copy the RDB file
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/dump-$DATE.rdb"
# Upload to S3 with server-side encryption
aws s3 cp "$BACKUP_DIR/dump-$DATE.rdb" "$S3_BUCKET/dump-$DATE.rdb" --sse aws:kms
# Verify upload
aws s3 ls "$S3_BUCKET/dump-$DATE.rdb"
# Cleanup local backups older than 7 days
find "$BACKUP_DIR" -name "dump-*.rdb" -mtime +7 -delete
echo "Backup completed: dump-$DATE.rdb"
Add to cron:
# /etc/cron.d/redis-backup
# Every 6 hours
0 */6 * * * redis /opt/scripts/redis-backup.sh >> /var/log/redis-backup.log 2>&1
Backup Retention Policy
Hourly backups: keep 24 (24 hours)
Daily backups: keep 30 (30 days)
Weekly backups: keep 52 (1 year)
Monthly backups: keep forever
Implement with S3 Lifecycle policies:
{
"Rules": [{
"Filter": {"Prefix": "redis/hourly/"},
"Expiration": {"Days": 2},
"Status": "Enabled"
}, {
"Filter": {"Prefix": "redis/daily/"},
"Expiration": {"Days": 30},
"Status": "Enabled"
}]
}
AOF Log Shipping
For near-real-time backup (RPO < 1 minute), ship the AOF file to durable storage continuously:
#!/bin/bash
# Ship new AOF data to S3 every minute
# Uses inotifywait to detect file changes
AOF_PATH="/var/lib/redis/appendonlydir"
S3_BUCKET="s3://my-company-backups/redis/aof"
inotifywait -m -e close_write "$AOF_PATH" | while read path action file; do
aws s3 sync "$AOF_PATH" "$S3_BUCKET/" --exclude "*.tmp"
echo "$(date): AOF shipped to S3"
done
For Redis 7.0+ multi-part AOF (RDB base + incremental AOF files):
# Ship only the incremental AOF files (the base is the RDB snapshot)
aws s3 sync /var/lib/redis/appendonlydir/ s3://backups/redis/aof/ \
--include "appendonly.aof.*.incr.aof"
BGSAVE and BGREWRITEAOF Interaction
If you're shipping both RDB snapshots and AOF files as backup layers, know what happens when they overlap: Redis will not run BGSAVE and BGREWRITEAOF at the same time. If a BGREWRITEAOF is already in progress and a scheduled BGSAVE fires (or vice versa), the second one is deferred — Redis logs a message and starts it automatically once the first finishes, rather than running both fork()s concurrently. This matters for backup scheduling in two ways:
- Don't assume your RDB backup cron job and your AOF rewrite trigger are independent. If they collide, your backup script needs to poll (
INFO persistence— checkrdb_bgsave_in_progress/aof_rewrite_in_progress) rather than assuming the snapshot completed on schedule. - Both operations fork a child process, and both children hold a copy-on-write reference to the dataset. If they did somehow overlap (they don't, by design, but a naive mental model might assume otherwise), you'd risk doubling the memory overhead from copy-on-write page duplication under write load — one more reason Redis serializes them rather than running both at once.
Sign in to keep reading
The rest of this module is free — sign in with Google to unlock it and track your progress.
Sign in & RegisterDiscussion
0Join the discussion