Skip to content

Database Recovery Runbook

Alert Description

Triggered when: RDS instance is unavailable, Backstage cannot connect to its Postgres database, or RDS CPU/storage is critically high.

Impact

  • Backstage DB down: Backstage portal is fully unavailable. Developers cannot scaffold services, view catalog, or read TechDocs.
  • Service DB down: Any service that depends on this DB returns errors.

Immediate Actions

1. Check RDS instance status

1
2
3
4
5
aws rds describe-db-instances \
  --db-instance-identifier idp-mvp-backstage \
  --region us-east-1 \
  --query 'DBInstances[0].{Status:DBInstanceStatus,Engine:Engine,Endpoint:Endpoint.Address}' \
  --output table

Expected status: available. If stopped, rebooting, or modifying — wait and recheck in 2 minutes.

2. Check CloudWatch metrics for the RDS instance

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# CPU utilisation
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name CPUUtilization \
  --dimensions Name=DBInstanceIdentifier,Value=idp-mvp-backstage \
  --start-time $(date -u -v-30M +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 300 \
  --statistics Average \
  --region us-east-1

# Free storage space
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name FreeStorageSpace \
  --dimensions Name=DBInstanceIdentifier,Value=idp-mvp-backstage \
  --start-time $(date -u -v-30M +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 300 \
  --statistics Average \
  --region us-east-1

3. If RDS is stopped — start it

1
2
3
aws rds start-db-instance \
  --db-instance-identifier idp-mvp-backstage \
  --region us-east-1

Wait ~5 minutes for the instance to become available.

4. If RDS is unavailable — reboot

1
2
3
aws rds reboot-db-instance \
  --db-instance-identifier idp-mvp-backstage \
  --region us-east-1

5. Verify Backstage can reconnect

1
2
3
4
5
6
7
8
# Restart Backstage pods to force a fresh connection
kubectl rollout restart deployment/backstage -n backstage

# Watch pods come up
kubectl get pods -n backstage -w

# Check Backstage logs for DB connection errors
kubectl logs deployment/backstage -n backstage --tail=50

Restore from RDS Snapshot

If the instance is corrupted or data is lost:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# List available snapshots
aws rds describe-db-snapshots \
  --db-instance-identifier idp-mvp-backstage \
  --region us-east-1 \
  --query 'DBSnapshots[*].{ID:DBSnapshotIdentifier,Time:SnapshotCreateTime,Status:Status}' \
  --output table

# Restore from the most recent snapshot to a new instance
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier idp-mvp-backstage-restored \
  --db-snapshot-identifier <snapshot-id> \
  --db-instance-class db.t3.micro \
  --region us-east-1 \
  --no-publicly-accessible

# Wait for the restored instance to be available (~10 min)
aws rds wait db-instance-available \
  --db-instance-identifier idp-mvp-backstage-restored \
  --region us-east-1

# Get the new endpoint
aws rds describe-db-instances \
  --db-instance-identifier idp-mvp-backstage-restored \
  --query 'DBInstances[0].Endpoint.Address' \
  --output text \
  --region us-east-1

Update the DB host in Secrets Manager and restart Backstage:

1
2
3
4
5
6
aws secretsmanager update-secret \
  --secret-id idp-mvp/backstage \
  --secret-string '{"POSTGRES_HOST":"<new-endpoint>","POSTGRES_PORT":"5432"}' \
  --region us-east-1

kubectl rollout restart deployment/backstage -n backstage

Storage Full — Emergency Cleanup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Connect to RDS via port-forward through a debug pod
kubectl run pg-debug --image=postgres:15 --restart=Never -n backstage -- sleep 3600
kubectl exec -it pg-debug -n backstage -- psql \
  -h <rds-endpoint> -U backstage -d backstage

-- Check table sizes
SELECT relname, pg_size_pretty(pg_total_relation_size(oid)) AS size
FROM pg_class WHERE relkind = 'r' ORDER BY pg_total_relation_size(oid) DESC LIMIT 10;

-- Vacuum to reclaim space
VACUUM FULL;

Alternatively, increase RDS storage via Terraform: update allocated_storage in terraform/rds.tf and run terraform apply.

Escalation

  • RDS unavailable for > 15 min and not recovering → open AWS Support ticket (severity: Urgent)
  • Data loss suspected → engage AWS Support before any write operations

Post-Incident

  • Verify automated backups are enabled (retention: 7 days, as configured in terraform/rds.tf)
  • Document the failure cause and restoration time in #incidents
  • Review CloudWatch alarms for RDS storage — add a low-storage alarm to terraform/finops.tf if missing