Backups and restore testing

How to check that every data store is backed up to policy, and how to prove it by restoring on a schedule.

Updated 2026-11-11Raw markdown

Strategy sections
On this page

Use this page for a backup coverage audit, a disaster-recovery exercise, or before any risky data change. A backup you have never restored is a hope, not a backup.

Define the policy per data class

Example starting point; replace the numbers with your own.

Data class RPO (max data loss) RTO (max downtime) Retention Copy
Production transactional DB 5 min (point-in-time) 1 h 35 days plus monthly for 1 year cross-region and cross-account
Production object storage 24 h 4 h versioning plus lifecycle cross-region replication
Non-production 24 h 1 day 7 days none

Retention may be set by regulation. Check before shortening anything.

Audit coverage with tags (read-only)

# Which RDS instances have automated backups and how long?
aws --profile <readonly-profile> --region <region> rds describe-db-instances \
  --query 'DBInstances[].{id:DBInstanceIdentifier,retention:BackupRetentionPeriod,encrypted:StorageEncrypted}' --output table

# What AWS Backup protects
aws --profile <readonly-profile> --region <region> backup list-protected-resources \
  --query 'Results[].{arn:ResourceArn,type:ResourceType,last:LastBackupTime}' --output table

# Buckets without versioning
for b in $(aws --profile <readonly-profile> s3api list-buckets --query 'Buckets[].Name' --output text); do
  v=$(aws --profile <readonly-profile> s3api get-bucket-versioning --bucket "$b" --query Status --output text 2>/dev/null)
  [ "$v" = "Enabled" ] || echo "$b versioning=$v"
done
  • Every production data store appears in the protected list.
  • A backup tag on each resource matches a backup plan selection rule.
  • Backup vaults use a separate key and, ideally, a separate account with vault lock.

Restore test runbook

Run at least quarterly per critical store.

  1. Pick a recovery point (not the newest one; pick something a week old).
  2. Restore into an isolated, non-production network with a new name.
  3. Check: it starts, row counts or object counts are in the expected range, the application can read it.
  4. Record the time it took. That is your real RTO.
  5. Delete the restored copy.
  6. Write the result in the ticket, with dates and durations.
# Example: restore an RDS snapshot into a test name (a write; needs approval)
aws --profile <restore-role-profile> --region <region> rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier <db>-restore-test-$(date +%Y%m%d) \
  --db-snapshot-identifier <snapshot-id> \
  --db-subnet-group-name <isolated-subnet-group> --no-publicly-accessible

Pitfalls

  • Backups in the same account as production. A compromised account loses both.
  • Encrypted backups whose KMS key can be deleted by the same admins.
  • Restore tests that restore into production networks and accidentally connect to live consumers.
  • Treating replication as backup. Replication copies deletes and corruption too.