Backup and Disaster Recovery Plan for Small Cloud Teams

Published 9 min read
backupdisaster recoveryrpo rtorunbooksincident response

Every small cloud team is one failed disk, one botched migration, or one ransomware incident away from a catastrophic data loss. A backup and disaster recovery plan is not optional -- it is the difference between a two-hour recovery and a company-ending event. This guide covers the 3-2-1 backup rule, RPO/RTO target-setting, automated backup scripts, restore testing, cross-region replication, disaster recovery drills, and communication plans sized for teams of 2-15 engineers.

Understanding RPO and RTO

Before choosing any backup tool or strategy, your team must define two numbers for each service tier:

  • RPO (Recovery Point Objective) -- the maximum acceptable data loss measured in time. An RPO of 1 hour means you can lose at most 1 hour of data.
  • RTO (Recovery Time Objective) -- the maximum acceptable downtime. An RTO of 30 minutes means the service must be back online within 30 minutes of an incident.

These targets drive every other decision. Here is a practical tier framework:

Service tierExamplesRPO targetRTO targetBackup frequency

Tier 1 - CriticalPrimary database, auth service5 minutes15 minutesContinuous WAL + hourly snapshots
Tier 2 - ImportantAPI servers, cache, queues1 hour1 hourHourly snapshots
Tier 3 - StandardInternal tools, staging24 hours4 hoursDaily snapshots
Tier 4 - Non-criticalDev environments, logs7 days24 hoursWeekly snapshots

Review your service inventory in Services Overview and assign each service a tier before proceeding.

The 3-2-1 Backup Rule

The 3-2-1 rule is the foundation of every reliable backup strategy:

  • 3 copies of your data (1 primary + 2 backups).
  • 2 different storage media or platforms.
  • 1 copy offsite (different geographic region or provider).

For cloud-native teams, this translates to:

  • Primary data in your production database (e.g., PostgreSQL on your cluster).
  • First backup as automated snapshots stored in the same cloud provider but a different region.
  • Second backup as encrypted exports pushed to a different cloud provider or object storage service.
  • Extended best practices:

    • Add a 3-2-1-1 layer: one copy should be immutable (write-once, cannot be deleted or modified for a retention period). This protects against ransomware.
    • Use object lock or WORM (Write Once Read Many) features available in S3, GCS, and Azure Blob.

    Automated Backup Scripts

    PostgreSQL Backup with pg_dump and S3

    #!/bin/bash
    

    <h1>backup-postgres.sh -- Automated PostgreSQL backup to S3</h1>

    set -euo pipefail

    DB_NAME="${DB_NAME:-appdb}"

    DB_HOST="${DB_HOST:-localhost}"

    DB_USER="${DB_USER:-app}"

    S3_BUCKET="${S3_BUCKET:-my-backups}"

    TIMESTAMP=$(date -u +"%Y%m%dT%H%M%SZ")

    BACKUP_FILE="/tmp/${DB_NAME}_${TIMESTAMP}.sql.gz"

    <h1>Create compressed backup</h1>

    pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" | gzip > "$BACKUP_FILE"

    <h1>Upload to S3 with server-side encryption</h1>

    aws s3 cp "$BACKUP_FILE" "s3://${S3_BUCKET}/postgres/${DB_NAME}/${TIMESTAMP}.sql.gz" \

    --sse AES256

    <h1>Upload to cross-region bucket (3-2-1 offsite copy)</h1>

    aws s3 cp "$BACKUP_FILE" "s3://${S3_BUCKET}-dr/postgres/${DB_NAME}/${TIMESTAMP}.sql.gz" \

    --sse AES256 --region us-west-2

    <h1>Cleanup local file</h1>

    rm -f "$BACKUP_FILE"

    <h1>Prune backups older than 30 days from primary bucket</h1>

    aws s3 ls "s3://${S3_BUCKET}/postgres/${DB_NAME}/" | \

    awk "{print \$4}" | while read -r key; do

    file_date=$(echo "$key" | grep -oP "\d{8}")

    cutoff=$(date -u -d "30 days ago" +"%Y%m%d")

    if [[ "$file_date" < "$cutoff" ]]; then

    aws s3 rm "s3://${S3_BUCKET}/postgres/${DB_NAME}/${key}"

    fi

    done

    echo "Backup completed: ${TIMESTAMP}"

    Continuous WAL Archiving for Tier 1 Services

    For critical databases where 5-minute RPO matters, use continuous WAL (Write-Ahead Log) archiving:

    <h1>postgresql.conf settings for WAL archiving</h1>
    

    archive_mode = on

    archive_command = 'aws s3 cp %p s3://my-backups/wal/%f --sse AES256'

    archive_timeout = 300 # Force archive every 5 minutes

    This lets you restore to any point in time within your retention window, not just to the last snapshot.

    Cron Schedule for Automated Backups

    <h1>/etc/cron.d/backups</h1>
    

    <h1>Tier 1: Every hour</h1>

    0 * * * * backup-user /opt/scripts/backup-postgres.sh >> /var/log/backup.log 2>&1

    <h1>Tier 2: Every 6 hours</h1>

    0 */6 * * * backup-user /opt/scripts/backup-redis.sh >> /var/log/backup.log 2>&1

    <h1>Tier 3: Daily at 2 AM UTC</h1>

    0 2 * * * backup-user /opt/scripts/backup-staging.sh >> /var/log/backup.log 2>&1

    <h1>Weekly integrity check (verify backup can be restored)</h1>

    0 4 * * 0 backup-user /opt/scripts/verify-restore.sh >> /var/log/backup.log 2>&1

    Restore Testing Procedures

    A backup that has never been restored is not a backup -- it is a hope. Restore testing must be scheduled and documented:

    Automated Restore Verification Script

    #!/bin/bash
    

    <h1>verify-restore.sh -- Restore latest backup and validate</h1>

    set -euo pipefail

    LATEST=$(aws s3 ls "s3://${S3_BUCKET}/postgres/appdb/"

    sorttail -1
    awk "{print \$4}")

    TEST_DB="restore_test_$(date +%s)"

    <h1>Download and restore</h1>

    aws s3 cp "s3://${S3_BUCKET}/postgres/appdb/${LATEST}" /tmp/restore.sql.gz

    gunzip -c /tmp/restore.sql.gz | psql -h localhost -U app -d "$TEST_DB"

    <h1>Validate row counts against production</h1>

    PROD_USERS=$(psql -h prod-host -U app -d appdb -t -c "SELECT count(*) FROM users")

    TEST_USERS=$(psql -h localhost -U app -d "$TEST_DB" -t -c "SELECT count(*) FROM users")

    if [[ "$PROD_USERS" -ne "$TEST_USERS" ]]; then

    echo "RESTORE VALIDATION FAILED: user count mismatch (prod: $PROD_USERS, test: $TEST_USERS)"

    exit 1

    fi

    <h1>Cleanup test database</h1>

    psql -h localhost -U app -c "DROP DATABASE ${TEST_DB}"

    rm -f /tmp/restore.sql.gz

    echo "Restore verification passed: ${LATEST}"

    Restore Testing Schedule

    Test typeFrequencyDurationWho runs it

    Automated restore checkWeekly10-15 min (automated)Cron job
    Manual point-in-time restoreMonthly1-2 hoursOn-call engineer
    Full failover drillQuarterly2-4 hoursWhole team
    Cross-region recoverySemiannually4-8 hoursWhole team + stakeholders

    Cross-Region Replication

    For Tier 1 services, cross-region replication reduces RTO by keeping a warm standby ready:

    <h1>S3 cross-region replication rule (via AWS CLI)</h1>
    

    aws s3api put-bucket-replication \

    --bucket my-backups \

    --replication-configuration '{

    "Role": "arn:aws:iam::role/s3-replication",

    "Rules": [{

    "Status": "Enabled",

    "Destination": {

    "Bucket": "arn:aws:s3:::my-backups-dr",

    "StorageClass": "STANDARD_IA"

    }

    }]

    }'

    For database-level replication, use streaming replication (PostgreSQL) or managed read replicas in a secondary region. Review infrastructure options at Cloud Computing.

    Disaster Recovery Drills

    Drills are the only way to know if your plan actually works. Small teams should run three types:

    1. Tabletop Drill (Monthly, 30 minutes)

    Walk through a scenario verbally without touching production:

    • "The primary database is corrupted. Walk me through recovery step by step."
    • "Our cloud provider has a region-wide outage. What do we do in the first 15 minutes?"
    • Identify gaps in runbooks, missing credentials, unclear ownership.

    2. Technical Restore Drill (Quarterly, 2-4 hours)

    Actually restore from backup to a test environment:

    • Restore the database from the latest backup.
    • Verify application functionality against the restored data.
    • Measure actual RTO and compare against target.
    • Document every step that was harder than expected.

    3. Full Failover Simulation (Semiannually, 4-8 hours)

    Simulate a complete region failure and recover in a secondary region:

    • Switch DNS to failover endpoints.
    • Restore services from cross-region backups.
    • Run application tests against the failover environment.
    • Measure actual RTO and RPO achieved.

    Communication Plan

    During an incident, communication failures cause as much damage as technical failures. Define these in advance:

    INCIDENT COMMUNICATION TEMPLATE
    
    

    Subject: [SEV-{1

    2
    3}] {Service} - {Brief description}

    Status: Investigating / Identified / Monitoring / Resolved

    Impact: {Who is affected and how}

    Start time: {UTC timestamp}

    Next update: {UTC timestamp, within 30 min for SEV1}

    What happened:

    {2-3 sentences}

    What we are doing:

    {Current actions}

    Customer action needed:

    {None / specific instructions}

    Communication channels by severity:

    SeverityInternal channelCustomer channelUpdate frequency

    SEV1 - CriticalWar room + phone treeStatus page + emailEvery 15 min
    SEV2 - MajorIncident Slack channelStatus pageEvery 30 min
    SEV3 - MinorTeam Slack channelNone (internal)Every 2 hours

    30-Day Starter Plan

    If your team has no DR plan today, follow this 30-day roadmap:

  • Week 1: Inventory all services and assign tiers. Define RPO/RTO targets for each tier.
  • Week 2: Implement automated backups for Tier 1 and Tier 2 services. Verify at least one restore works.
  • Week 3: Set up backup monitoring and alerting (alert if any scheduled backup fails or is late). Write the first version of your incident communication template.
  • Week 4: Run your first tabletop drill. Document all gaps found and create tasks to fix them.
  • Check your current infrastructure at Services Overview and benchmark backup costs at Pricing.

    Frequently Asked Questions

    How much should a small team spend on backup infrastructure?

    Budget 5-15% of your total infrastructure spend on backups and DR. For a team spending $500/month on cloud, that is $25-75/month on backup storage and tooling. Object storage (S3, GCS) is cheap -- most teams spend more on the engineering time to set up and test than on the storage itself.

    Is cloud provider snapshots enough for disaster recovery?

    Cloud snapshots are a good start but not sufficient alone. Snapshots are typically stored in the same region as the source. A region-wide outage would take out both your data and your snapshots. Always have at least one copy in a different region or provider.

    How often should we actually test restores?

    Automated restore verification should run weekly. Manual point-in-time restore tests should happen monthly. Full failover drills should happen quarterly. If you are not testing restores at least monthly, your backups are aspirational, not operational.

    What is the difference between backup and disaster recovery?

    Backup is copying data to a safe location. Disaster recovery is the entire plan for getting your services running again after a failure, which includes backups but also covers networking, DNS failover, application deployment, data validation, and team communication. You need both.

    Should we use a managed backup service or build our own?

    Start with managed services (cloud provider snapshots, managed database backups) for speed. Add custom scripts only for gaps the managed service does not cover, such as cross-provider copies or application-level consistency checks. Building everything from scratch is rarely justified for teams under 10 engineers.

    How do we handle backup encryption?

    Encrypt all backups at rest using server-side encryption (SSE-S3 or SSE-KMS). For cross-provider copies, encrypt client-side before upload using GPG or age. Store encryption keys separately from the backups -- ideally in a different provider or a hardware security module.

    Final Takeaway

    A disaster recovery plan for small teams does not need enterprise complexity. It needs clear RPO/RTO targets, automated backups with verified restores, a communication template, and regular drills. The gap between "we have backups" and "we can recover" is measured entirely in testing. Start your 30-day plan today.

    Need tailored guidance for your stack? Contact us.