Backup and Recovery Procedures
Product: Nquiry - Investigation Management Platform Last Updated: January 30, 2026 Version: 1.0 Owner: Joe Etherage (Founder)
1. Overview
This document describes backup and recovery procedures for Nquiry's data stores. It ensures business continuity and compliance with FedRAMP CP (Contingency Planning) controls.
Data Stores Covered
| Store | Type | Backup Method | RPO | RTO |
|---|---|---|---|---|
| RDS PostgreSQL | Relational database | Automated snapshots + PITR | 5 minutes | < 1 hour |
| S3 Evidence | Object storage | Versioning + replication | 0 (immediate) | < 15 minutes |
| Cognito | User directory | AWS managed | N/A | N/A |
RPO = Recovery Point Objective (max data loss) RTO = Recovery Time Objective (max downtime)
2. RDS PostgreSQL Backup
2.1 Automated Backups (Enabled)
Current Configuration:
| Setting | Dev | Production (Planned) |
|---|---|---|
| Backup retention | 7 days | 35 days |
| Backup window | 03:00-04:00 UTC | 03:00-04:00 UTC |
| Multi-AZ | No | Yes |
| Storage encrypted | Yes (KMS) | Yes (KMS) |
What's Backed Up:
- Full database snapshot (daily)
- Transaction logs (every 5 minutes) for Point-in-Time Recovery
2.2 Manual Snapshots
Create manual snapshots before major changes:
# Create manual snapshot
aws rds create-db-snapshot \
--db-instance-identifier invapp-dev-postgres \
--db-snapshot-identifier manual-$(date +%Y%m%d-%H%M%S) \
--region us-east-1
# List available snapshots
aws rds describe-db-snapshots \
--db-instance-identifier invapp-dev-postgres \
--query 'DBSnapshots[*].[DBSnapshotIdentifier,SnapshotCreateTime,Status]' \
--output table
# Delete old manual snapshot (automated ones are managed by retention)
aws rds delete-db-snapshot \
--db-snapshot-identifier <snapshot-id>
2.3 Verify Backups
Monthly verification checklist:
- Automated backups completing successfully (CloudWatch → RDS Events)
- At least one manual snapshot exists from current month
- Snapshot retention matches policy
- Test restore to temporary instance (quarterly)
3. RDS Recovery Procedures
3.1 Point-in-Time Recovery (PITR)
Use case: Recover from accidental data deletion or corruption
# Restore to a specific point in time
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier invapp-dev-postgres \
--target-db-instance-identifier invapp-dev-postgres-restored \
--restore-time "2026-01-30T15:30:00Z" \
--db-instance-class db.t3.micro \
--vpc-security-group-ids sg-XXXXXXXXX \
--db-subnet-group-name invapp-dev-db-subnet-group \
--no-multi-az \
--publicly-accessible
Post-restore steps:
- Wait for instance to become available (~10-20 minutes)
- Verify data integrity
- Update application to point to restored instance (or rename instances)
- Delete old instance if recovery is complete
3.2 Restore from Snapshot
Use case: Complete database recovery or environment cloning
# List available snapshots
aws rds describe-db-snapshots \
--db-instance-identifier invapp-dev-postgres \
--query 'DBSnapshots[*].[DBSnapshotIdentifier,SnapshotCreateTime]' \
--output table
# Restore from snapshot
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier invapp-dev-postgres-restored \
--db-snapshot-identifier <snapshot-id> \
--db-instance-class db.t3.micro \
--vpc-security-group-ids sg-XXXXXXXXX \
--db-subnet-group-name invapp-dev-db-subnet-group \
--no-multi-az \
--publicly-accessible
3.3 Instance Replacement (Rename Method)
Use case: Replace failed instance with restored version
# 1. Restore to temporary name
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier invapp-dev-postgres-new \
--db-snapshot-identifier <snapshot-id> \
...
# 2. Wait for new instance to be available
aws rds wait db-instance-available \
--db-instance-identifier invapp-dev-postgres-new
# 3. Rename old instance (if still exists)
aws rds modify-db-instance \
--db-instance-identifier invapp-dev-postgres \
--new-db-instance-identifier invapp-dev-postgres-old \
--apply-immediately
# 4. Wait for rename
sleep 60
# 5. Rename new instance to production name
aws rds modify-db-instance \
--db-instance-identifier invapp-dev-postgres-new \
--new-db-instance-identifier invapp-dev-postgres \
--apply-immediately
# 6. Update DNS/connection strings if needed
# (Amplify env vars point to instance identifier, may need update)
# 7. Delete old instance after verification
aws rds delete-db-instance \
--db-instance-identifier invapp-dev-postgres-old \
--skip-final-snapshot
4. S3 Evidence Backup
4.1 Versioning (Enabled)
All evidence buckets have versioning enabled:
# Verify versioning status
aws s3api get-bucket-versioning \
--bucket investigation-app-dev-760007728097
Recovery from accidental deletion:
# List object versions (including delete markers)
aws s3api list-object-versions \
--bucket investigation-app-dev-760007728097 \
--prefix "org-id/investigation-id/evidence-id/"
# Restore deleted object (remove delete marker)
aws s3api delete-object \
--bucket investigation-app-dev-760007728097 \
--key "path/to/file.pdf" \
--version-id "delete-marker-version-id"
# Or restore specific version
aws s3api copy-object \
--bucket investigation-app-dev-760007728097 \
--copy-source "investigation-app-dev-760007728097/path/to/file.pdf?versionId=old-version-id" \
--key "path/to/file.pdf"
4.2 Cross-Region Replication (Production - Planned)
For production, enable replication to us-west-2:
# Terraform configuration (future)
resource "aws_s3_bucket_replication_configuration" "evidence" {
bucket = aws_s3_bucket.main.id
role = aws_iam_role.replication.arn
rule {
id = "disaster-recovery"
status = "Enabled"
destination {
bucket = "arn:aws:s3:::investigation-app-prod-dr"
storage_class = "STANDARD_IA"
}
}
}
4.3 Lifecycle Rules (Current)
- Transition to IA: 90 days (cost optimization)
- Delete incomplete multipart uploads: 7 days
- Delete old versions: 90 days (configurable)
5. Cognito Backup
Cognito user data is managed by AWS and cannot be directly backed up. However:
5.1 User Export
Export user list for offline reference:
# Export users to CSV
aws cognito-idp list-users \
--user-pool-id us-east-1_C8qcVKKp5 \
--output json > cognito-users-$(date +%Y%m%d).json
# Parse to CSV
cat cognito-users-$(date +%Y%m%d).json | \
jq -r '.Users[] | [.Username, .UserStatus, (.Attributes[] | select(.Name=="email") | .Value)] | @csv' \
> cognito-users-$(date +%Y%m%d).csv
5.2 Recovery Options
| Scenario | Solution |
|---|---|
| Deleted user pool | Contact AWS Support (no self-service restore) |
| Deleted user | Re-create via admin API, user re-verifies email |
| Compromised user | Disable user, reset password, revoke tokens |
6. Application State Recovery
6.1 Infrastructure (Terraform)
All infrastructure is defined in code:
# Restore infrastructure to known state
cd infrastructure/terraform/environments/dev
terraform init
terraform plan
terraform apply
6.2 Application Code
# Clone from GitHub
git clone git@github.com:AlligatorC0der/investigation-app.git
# Install dependencies
npm install
# Set environment variables (from secure storage)
cp .env.example .env.local
# Edit with production values
# Deploy
git push origin main # Triggers Amplify deployment
7. Disaster Recovery Scenarios
Scenario A: Accidental Data Deletion
Symptoms: User reports missing investigation/evidence
Response:
-
Check audit log for deletion event
SELECT * FROM audit_logWHERE resource_type = 'investigation'AND action = 'delete'ORDER BY created_at DESC LIMIT 10; -
Determine scope (single record vs. bulk)
-
For database records:
- PITR restore to before deletion
- Extract needed records
- Import to production
-
For S3 files:
- Restore from version history
- Or restore from cross-region replica
Scenario B: Database Corruption
Symptoms: Application errors, inconsistent data
Response:
- Assess scope of corruption
- Take immediate snapshot of current state (forensics)
- Identify last known good backup
- Perform PITR to before corruption
- Validate data integrity
- Switch to restored instance
- Investigate root cause
Scenario C: Complete Region Failure
Symptoms: All AWS us-east-1 services unavailable
Response (Production):
- Activate DR plan
- Promote cross-region S3 replica to primary
- Restore RDS from cross-region snapshot
- Update DNS to DR region
- Communicate status to customers
Note: Currently dev-only in single region. Production will have multi-region DR.
Scenario D: Ransomware/Compromise
Symptoms: Encrypted files, unauthorized changes, ransom demand
Response:
- Isolate immediately - Disable all access (see incident response plan)
- Do NOT pay ransom
- Determine attack timeline via CloudTrail/audit logs
- Identify last clean backup (before compromise)
- Restore from clean backup to isolated environment
- Validate no persistence mechanisms
- Restore service with enhanced security
- Report to authorities (FBI IC3, US-CERT)
8. Recovery Testing
Quarterly Test: Database Restore
| Step | Action | Expected Result |
|---|---|---|
| 1 | Create test snapshot | Snapshot created in < 10 min |
| 2 | Restore to new instance | Instance available in < 30 min |
| 3 | Connect and verify data | All tables present, recent data exists |
| 4 | Run application health check | API responds correctly |
| 5 | Delete test instance | Clean up complete |
Test Schedule:
- Q1: January
- Q2: April
- Q3: July
- Q4: October
Last Tested: TBD (schedule first test for Q2 2026)
Annual Test: Full DR Drill
- Restore all data stores
- Deploy application to restored infrastructure
- Validate end-to-end functionality
- Document recovery time achieved
- Update procedures based on lessons learned
9. Monitoring & Alerts
CloudWatch Alarms (Recommended)
# Create alarm for backup failures
aws cloudwatch put-metric-alarm \
--alarm-name "rds-backup-failed" \
--alarm-description "RDS automated backup failed" \
--metric-name "BackupRetentionPeriodStorageUsed" \
--namespace "AWS/RDS" \
--statistic "Average" \
--period 86400 \
--threshold 0 \
--comparison-operator "LessThanThreshold" \
--evaluation-periods 1 \
--alarm-actions "arn:aws:sns:us-east-1:ACCOUNT:alerts"
Events to Monitor
| Event | Source | Action |
|---|---|---|
| Backup completed | RDS Events | Informational |
| Backup failed | RDS Events | Alert + investigate |
| Snapshot deleted | CloudTrail | Audit (verify authorized) |
| Restore initiated | RDS Events | Alert (verify authorized) |
10. Quick Reference
Key Commands
# === RDS ===
# List snapshots
aws rds describe-db-snapshots --db-instance-identifier invapp-dev-postgres
# Create manual snapshot
aws rds create-db-snapshot --db-instance-identifier invapp-dev-postgres --db-snapshot-identifier manual-YYYYMMDD
# Point-in-time restore
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier invapp-dev-postgres \
--target-db-instance-identifier invapp-restored \
--restore-time "2026-01-30T15:00:00Z"
# Restore from snapshot
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier invapp-restored \
--db-snapshot-identifier <snapshot-id>
# === S3 ===
# List object versions
aws s3api list-object-versions --bucket investigation-app-dev-760007728097 --prefix "path/"
# Restore deleted object
aws s3api delete-object --bucket BUCKET --key KEY --version-id DELETE_MARKER_ID
# === Cognito ===
# Export users
aws cognito-idp list-users --user-pool-id us-east-1_C8qcVKKp5 > users.json
Instance Identifiers
| Environment | RDS Instance | S3 Bucket | Cognito Pool |
|---|---|---|---|
| Dev | invapp-dev-postgres | investigation-app-dev-760007728097 | us-east-1_C8qcVKKp5 |
| Staging | TBD | TBD | TBD |
| Production | TBD | TBD | TBD |
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-30 | Joe Etherage | Initial version |