Backup and Restore¶
Production is the launch-content environment (see Deployment), so its data must be recoverable after an accidental delete, a bad migration, or an overwritten file. This guide lists what is backed up, the retention, how to restore each store, and who owns it.
Dev is throwaway and is not covered here. Its database keeps one day of backups and its media bucket is unversioned by design.
What is backed up (production)¶
| Store | Mechanism | Retention | Recovers from |
|---|---|---|---|
Database — Aurora cslewis-app-db-prod |
Automated backups + point-in-time restore (PITR) | 7 days | Dropped rows, a bad migration, table corruption |
Media — S3 cslewis-uploads-prod (Wagtail images, generated audio) |
Object versioning, prod only | Prior versions kept 90 days, then expired | An overwritten or deleted media object |
| Redis (ElastiCache) | Not backed up | n/a | Nothing. It holds cache and sessions and rebuilds itself. |
Two extra safeguards on the database: deletion protection is on, so the cluster cannot be deleted by accident, and stack teardown takes a final snapshot (RemovalPolicy.SNAPSHOT).
Media versioning is set in .aws/iac/lib/storage-stack.ts (versioned: isProd) with a 90-day noncurrent-version expiry to bound storage cost. It applies to production only.
Restore the database (Aurora PITR)¶
PITR restores to a new cluster; it cannot overwrite the live one in place. Plan a short cutover.
- Pick the target time (any point within the last 7 days).
- Read the live cluster's network config. The cluster runs in isolated subnets and accepts only the compute security group, so a restore without these lands in the default network and is unreachable:
SUBNET_GROUP=$(aws rds describe-db-clusters --profile cslewis --region us-east-2 \ --db-cluster-identifier cslewis-app-db-prod --query "DBClusters[0].DBSubnetGroup" --output text) SG=$(aws rds describe-db-clusters --profile cslewis --region us-east-2 \ --db-cluster-identifier cslewis-app-db-prod --query "DBClusters[0].VpcSecurityGroups[0].VpcSecurityGroupId" --output text) - Restore to a new cluster in that same network:
aws rds restore-db-cluster-to-point-in-time \ --profile cslewis --region us-east-2 \ --source-db-cluster-identifier cslewis-app-db-prod \ --db-cluster-identifier cslewis-app-db-prod-restore \ --restore-to-time <ISO-8601-timestamp> \ --db-subnet-group-name "$SUBNET_GROUP" \ --vpc-security-group-ids "$SG" - Add a Serverless v2 instance to the restored cluster, then verify the data.
- Cut over: update
DATABASE_URLin thecslewis-app-prodsecret to the restored endpoint, then force a new ECS deployment. A task reads the secret only at start, so a secret change alone leaves live tasks on the old cluster. For a partial restore, export the needed rows from the restored cluster into the live one instead. - Delete the temporary cluster once the restore is confirmed.
Restore a media object (S3 version)¶
A deleted object leaves a delete marker; an overwrite leaves the old bytes as a prior version. Both are recoverable while the version is inside the 90-day window.
- List the versions of the key:
- Recover the content:
- Overwritten: copy the prior
VersionIdback over the current key. - Deleted: delete the delete marker to bring the object back.
- Invalidate the CloudFront path. Media is served through CloudFront (
media-cdn-prod, distributionE2D6DSI04HRA0T) with a caching-optimized policy, so restoring the S3 object does not refresh the edge; the stale copy serves until the cache expires.If the id changes, read it withaws cloudfront create-invalidation --profile cslewis \ --distribution-id E2D6DSI04HRA0T --paths "/<object-key>"aws cloudfront list-distributions --query "DistributionList.Items[?contains(Comment,'cslewis-app-media-prod')].Id".
Ownership¶
The backend / DevOps owner runs these restores and owns the runbook. Raise a restore request in the team channel with the store, the object or time range, and the reason. Run a restore drill before public launch (tracked on the Launch Checklist).