· nervico-team · arquitectura-cloud  · 11 min read

DR y backup en AWS: estrategia práctica para startups

Estrategia práctica de disaster recovery y backup en AWS para startups: niveles de protección, RPO/RTO reales, automatización con Terraform y costes por escenario.

Estrategia práctica de disaster recovery y backup en AWS para startups: niveles de protección, RPO/RTO reales, automatización con Terraform y costes por escenario.

Las startups no quiebran por no tener un plan de disaster recovery. Quiebran porque asumieron que nunca lo necesitarían. Y cuando la base de datos se corrompe, un empleado borra el bucket de producción, o una región de AWS sufre una interrupción de 4 horas, descubren que no tienen forma de recuperar el servicio.

Según un estudio de Zetta, el 40% de las empresas que pierden sus datos de forma permanente cierran en los siguientes 6 meses. Y no se trata solo de desastres naturales. La causa más común de pérdida de datos en cloud es el error humano: un DROP TABLE en la base de datos equivocada, un terraform destroy sin confirmar el workspace, o una actualización de software que corrompe los datos.

Este artículo presenta una estrategia de DR y backup progresiva para startups en AWS: empezando por lo esencial con presupuesto mínimo y escalando conforme crece la criticidad del negocio.

Conceptos fundamentales

RPO y RTO: las dos métricas que importan

RPO (Recovery Point Objective): Cuántos datos puedes permitirte perder. Si tu RPO es 1 hora, necesitas backups al menos cada hora. Si es 0, necesitas replicación en tiempo real.

RTO (Recovery Time Objective): Cuánto tiempo puedes estar sin servicio. Si tu RTO es 4 horas, necesitas poder restaurar el servicio completo en menos de 4 horas. Si es 0, necesitas infraestructura activa en otra región.

RPO ←──── Pérdida de datos aceptable ────→ Momento del desastre
                                                    |
                                           RTO ←────→ Servicio restaurado
Nivel de negocioRPO típicoRTO típicoEjemplo
Blog/marketing24 horas24 horasWeb corporativa
SaaS early-stage1-4 horas4-8 horasMVP con primeros clientes
SaaS en crecimiento15-60 minutos1-4 horasProducto con revenue
SaaS enterprise1-5 minutos15-60 minutosClientes con SLA contractual
Finanzas/saludSegundosMinutosDatos regulados

Las 4 estrategias de DR en AWS

AWS define cuatro estrategias de DR, ordenadas de menor a mayor coste y menor a mayor velocidad de recuperación:

1. Backup and Restore (coste bajo, RTO alto):

Backups regulares almacenados en otra región. En caso de desastre, restauras la infraestructura y los datos desde los backups. RTO: horas. RPO: depende de la frecuencia de backup.

2. Pilot Light (coste bajo-medio, RTO medio):

La infraestructura esencial (base de datos replicada, configuración) está siempre activa en la región secundaria. En caso de desastre, arrancas el resto de la infraestructura (compute, load balancers). RTO: 30-60 minutos.

3. Warm Standby (coste medio, RTO bajo):

Una versión reducida de la infraestructura completa está siempre activa en la región secundaria. En caso de desastre, escalas al tamaño de producción. RTO: 10-30 minutos.

4. Active-Active (coste alto, RTO mínimo):

Infraestructura completa en dos o más regiones, sirviendo tráfico simultáneamente. En caso de desastre, el tráfico se redirige automáticamente. RTO: segundos.

Estrategia para startups: enfoque progresivo

Nivel 1: lo mínimo imprescindible (0-50 dolares/mes)

Si estás en fase pre-revenue o early-stage, no necesitas active-active multi-región. Pero sí necesitas protegerte contra las causas más comunes de pérdida de datos: error humano y corrupción.

Backups automatizados de RDS:

RDS incluye backups automatizados sin coste adicional (más allá del almacenamiento):

# Verificar que los backups automáticos están activos
aws rds describe-db-instances \
  --db-instance-identifier my-database \
  --query 'DBInstances[0].{BackupRetention:BackupRetentionPeriod,BackupWindow:PreferredBackupWindow}'

Configura la retención a un mínimo de 7 días (el máximo es 35). Los backups se almacenan en S3 gestionado por AWS y permiten restaurar a cualquier punto en el tiempo (PITR) dentro del período de retención.

# Terraform: RDS con backup configurado
resource "aws_db_instance" "production" {
  identifier                = "production-db"
  engine                    = "postgres"
  engine_version            = "16.1"
  instance_class            = "db.t4g.medium"
  allocated_storage         = 50
  storage_encrypted         = true

  backup_retention_period   = 14        # 14 días de retención
  backup_window             = "03:00-04:00"  # Ventana de backup (UTC)
  maintenance_window        = "Mon:04:00-Mon:05:00"

  deletion_protection       = true      # Evita borrado accidental
  skip_final_snapshot       = false
  final_snapshot_identifier = "production-db-final"
}

Versionado en S3:

S3 Versioning mantiene todas las versiones de cada objeto. Si alguien sobreescribe o borra un archivo, puedes restaurar la versión anterior.

resource "aws_s3_bucket_versioning" "production" {
  bucket = aws_s3_bucket.production.id

  versioning_configuration {
    status = "Enabled"
  }
}

# Lifecycle rule: mover versiones antiguas a Glacier después de 30 días
resource "aws_s3_bucket_lifecycle_configuration" "production" {
  bucket = aws_s3_bucket.production.id

  rule {
    id     = "archive-old-versions"
    status = "Enabled"

    noncurrent_version_transition {
      noncurrent_days = 30
      storage_class   = "GLACIER"
    }

    noncurrent_version_expiration {
      noncurrent_days = 365
    }
  }
}

MFA Delete para protección extra:

Habilita MFA Delete en buckets críticos. Requiere autenticación MFA para borrar versiones o desactivar el versionado.

AWS Backup para centralizar:

AWS Backup permite gestionar todos los backups desde un punto central:

# Plan de backup centralizado
resource "aws_backup_plan" "daily" {
  name = "daily-backup-plan"

  rule {
    rule_name         = "daily-backup"
    target_vault_name = aws_backup_vault.production.name
    schedule          = "cron(0 3 * * ? *)"  # Diario a las 3:00 UTC

    lifecycle {
      delete_after = 30  # Retener 30 días
    }

    copy_action {
      destination_vault_arn = aws_backup_vault.dr_region.arn
      lifecycle {
        delete_after = 14  # Retener copias 14 días en región DR
      }
    }
  }
}

# Selección de recursos a proteger
resource "aws_backup_selection" "all_critical" {
  iam_role_arn = aws_iam_role.backup_role.arn
  name         = "critical-resources"
  plan_id      = aws_backup_plan.daily.id

  selection_tag {
    type  = "STRINGEQUALS"
    key   = "Backup"
    value = "true"
  }
}

Coste de Nivel 1: Con una base de datos de 50 GB y 100 GB en S3, el coste adicional de backups es inferior a 20 dolares/mes.

Nivel 2: protección cross-region (50-200 dolares/mes)

Cuando tienes clientes que pagan y un SLA implícito (o explícito), necesitas protegerte contra la pérdida de una región completa de AWS. Ocurre raramente, pero las interrupciones regionales de 1-4 horas han ocurrido varias veces en la historia de AWS.

Replicación de RDS a otra región:

# Read Replica cross-region (sirve como base para DR)
resource "aws_db_instance" "dr_replica" {
  provider = aws.dr_region

  replicate_source_db    = aws_db_instance.production.arn
  instance_class         = "db.t4g.medium"
  storage_encrypted      = true
  kms_key_id             = aws_kms_key.dr_rds.arn
  publicly_accessible    = false
  vpc_security_group_ids = [aws_security_group.dr_rds.id]
  db_subnet_group_name   = aws_db_subnet_group.dr.name

  tags = {
    Name = "dr-replica"
    Role = "disaster-recovery"
  }
}

La réplica cross-region tiene un lag de replicación de segundos a minutos. En caso de desastre, promueves la réplica a instancia primaria:

# Promover réplica a primaria (en la región DR)
aws rds promote-read-replica \
  --db-instance-identifier dr-replica \
  --region eu-central-1

Replicación de S3 cross-region:

resource "aws_s3_bucket_replication_configuration" "production" {
  bucket = aws_s3_bucket.production.id
  role   = aws_iam_role.replication.arn

  rule {
    id     = "replicate-all"
    status = "Enabled"

    destination {
      bucket        = aws_s3_bucket.dr.arn
      storage_class = "STANDARD_IA"
    }
  }
}

Snapshots de EBS cross-region:

Para volúmenes EBS (EC2), automatiza la copia de snapshots a la región DR:

import boto3

ec2_source = boto3.client('ec2', region_name='eu-west-1')
ec2_dr = boto3.client('ec2', region_name='eu-central-1')

def copy_latest_snapshots():
    # Encontrar snapshots recientes
    snapshots = ec2_source.describe_snapshots(
        Filters=[
            {'Name': 'tag:Backup', 'Values': ['true']},
            {'Name': 'status', 'Values': ['completed']}
        ],
        OwnerIds=['self']
    )

    for snap in snapshots['Snapshots']:
        # Copiar a región DR
        ec2_dr.copy_snapshot(
            SourceRegion='eu-west-1',
            SourceSnapshotId=snap['SnapshotId'],
            Description=f"DR copy of {snap['SnapshotId']}",
            Encrypted=True,
            KmsKeyId='alias/dr-ebs-key'
        )

Nivel 3: pilot light multi-región (200-500 dolares/mes)

Para startups con revenue significativo y SLAs contractuales con clientes.

Infraestructura en región DR:

# VPC en región DR (siempre activa)
module "dr_vpc" {
  source  = "./modules/vpc"
  providers = { aws = aws.dr_region }

  cidr_block = "10.1.0.0/16"
  name       = "dr-vpc"
}

# RDS réplica cross-region (siempre activa)
# Ya configurada en Nivel 2

# ECS cluster (definido pero sin tareas activas)
resource "aws_ecs_cluster" "dr" {
  provider = aws.dr_region
  name     = "dr-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

# ALB (definido pero sin targets activos)
resource "aws_lb" "dr" {
  provider           = aws.dr_region
  name               = "dr-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.dr_alb.id]
  subnets            = module.dr_vpc.public_subnet_ids
}

Failover con Route 53 Health Checks:

# Health check del servicio principal
resource "aws_route53_health_check" "primary" {
  fqdn              = "api.tudominio.com"
  port               = 443
  type               = "HTTPS"
  resource_path      = "/health"
  failure_threshold  = 3
  request_interval   = 30

  tags = {
    Name = "primary-health-check"
  }
}

# DNS failover
resource "aws_route53_record" "api" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.tudominio.com"
  type    = "A"

  alias {
    name                   = aws_lb.primary.dns_name
    zone_id                = aws_lb.primary.zone_id
    evaluate_target_health = true
  }

  set_identifier = "primary"
  failover_routing_policy {
    type = "PRIMARY"
  }

  health_check_id = aws_route53_health_check.primary.id
}

resource "aws_route53_record" "api_dr" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.tudominio.com"
  type    = "A"

  alias {
    name                   = aws_lb.dr.dns_name
    zone_id                = aws_lb.dr.zone_id
    evaluate_target_health = false
  }

  set_identifier = "secondary"
  failover_routing_policy {
    type = "SECONDARY"
  }
}

Runbook de activación DR:

El pilot light necesita un runbook documentado y probado:

#!/bin/bash
# runbook-activate-dr.sh
# Ejecutar cuando la región primaria no está disponible

echo "1. Promoviendo réplica de base de datos..."
aws rds promote-read-replica \
  --db-instance-identifier dr-replica \
  --region eu-central-1

echo "2. Escalando servicios ECS en región DR..."
aws ecs update-service \
  --cluster dr-cluster \
  --service api-service \
  --desired-count 3 \
  --region eu-central-1

echo "3. Verificando health check del ALB DR..."
aws elbv2 describe-target-health \
  --target-group-arn arn:aws:elasticloadbalancing:eu-central-1:123456789:targetgroup/dr-api/abc \
  --region eu-central-1

echo "4. Route 53 debería failover automáticamente."
echo "   Verificar resolución DNS..."
dig api.tudominio.com +short

echo "DR activado. Monitorizar durante las próximas 24 horas."

Automatización de backups: lo que no se automatiza no se hace

AWS Backup con notificaciones

# SNS topic para alertas de backup
resource "aws_sns_topic" "backup_alerts" {
  name = "backup-failure-alerts"
}

resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.backup_alerts.arn
  protocol  = "email"
  endpoint  = "ops@tuempresa.com"
}

# EventBridge rule para fallos de backup
resource "aws_cloudwatch_event_rule" "backup_failure" {
  name = "backup-job-failure"

  event_pattern = jsonencode({
    source      = ["aws.backup"]
    detail-type = ["Backup Job State Change"]
    detail = {
      state = ["FAILED", "EXPIRED"]
    }
  })
}

resource "aws_cloudwatch_event_target" "sns" {
  rule      = aws_cloudwatch_event_rule.backup_failure.name
  target_id = "send-to-sns"
  arn       = aws_sns_topic.backup_alerts.arn
}

Verificación periódica de restauración

Un backup que no se ha probado no es un backup. Es una esperanza. Automatiza pruebas de restauración mensuales:

import boto3
from datetime import datetime

rds = boto3.client('rds', region_name='eu-west-1')

def test_restore():
    timestamp = datetime.utcnow().strftime('%Y%m%d%H%M')
    test_instance = f"restore-test-{timestamp}"

    # Restaurar desde el último snapshot
    snapshots = rds.describe_db_snapshots(
        DBInstanceIdentifier='production-db',
        SnapshotType='automated'
    )

    latest = sorted(
        snapshots['DBSnapshots'],
        key=lambda x: x['SnapshotCreateTime'],
        reverse=True
    )[0]

    print(f"Restaurando desde snapshot: {latest['DBSnapshotIdentifier']}")

    rds.restore_db_instance_from_db_snapshot(
        DBInstanceIdentifier=test_instance,
        DBSnapshotIdentifier=latest['DBSnapshotIdentifier'],
        DBInstanceClass='db.t4g.micro',
        PubliclyAccessible=False
    )

    # Esperar a que esté disponible, ejecutar verificaciones, y borrar
    print(f"Instancia de test creada: {test_instance}")
    print("Verificar manualmente y borrar con:")
    print(f"  aws rds delete-db-instance --db-instance-identifier {test_instance} --skip-final-snapshot")

Costes reales por nivel de protección

NivelEstrategiaRPORTOCoste adicional/mes
1Backup and Restore (misma región)1-24 horas4-24 horas10-50 $
2Backup cross-region1-4 horas2-8 horas50-200 $
3Pilot Light5-60 minutos30-60 minutos200-500 $
4Warm Standby1-5 minutos10-30 minutos500-2.000 $
5Active-ActiveSegundosSegundos2.000-10.000 $

Errores frecuentes

Error 1: confiar solo en backups automáticos de RDS

Los backups automáticos de RDS se eliminan cuando borras la instancia. Si alguien ejecuta terraform destroy por error o borra la instancia manualmente, los backups desaparecen. Solución: crea snapshots manuales adicionales y cópialos a otra región.

Error 2: no proteger contra borrado accidental

La causa principal de pérdida de datos en cloud no es una catástrofe. Es un humano con permisos excesivos. Activa:

  • deletion_protection en RDS
  • S3 Versioning + MFA Delete
  • prevent_destroy en Terraform para recursos críticos
  • IAM policies restrictivas (no AdministratorAccess para desarrolladores)

Error 3: no probar la restauración

Hemos visto empresas con backups perfectamente configurados que descubrieron, durante un incidente real, que la restauración fallaba porque el formato había cambiado, los permisos eran incorrectos o el proceso no estaba documentado. Prueba la restauración al menos una vez al trimestre.

Error 4: backup sin cifrado

Si tus datos de producción están cifrados (y deberían estarlo), los backups deben estar cifrados con la misma política. Un backup sin cifrar en S3 es una vulnerabilidad, especialmente si los reglamentos exigen protección de datos (GDPR, HIPAA).

Error 5: no considerar los datos fuera de AWS

Si tu aplicación genera datos que se almacenan en servicios externos (Stripe, Firebase, Algolia, ElasticSearch gestionado), esos datos necesitan su propia estrategia de backup. No asumas que el proveedor SaaS mantiene backups accesibles.

Checklist de DR para startups

Inmediato (semana 1):

  • Backups automáticos de RDS activados con retención de 14 días
  • S3 Versioning activado en buckets de producción
  • Deletion protection en RDS y buckets críticos
  • Alertas de fallo de backup configuradas

Corto plazo (mes 1):

  • AWS Backup configurado para todos los recursos críticos
  • Copia de backups a una segunda región
  • Runbook de restauración documentado
  • Primera prueba de restauración completada

Medio plazo (trimestre 1):

  • Réplica cross-region de RDS para datos críticos
  • Replicación cross-region de S3
  • Health checks de Route 53 configurados
  • Prueba de restauración trimestral automatizada

Largo plazo (según crecimiento):

  • Pilot light o warm standby en región secundaria
  • Failover automatizado con Route 53
  • Ejercicios de DR simulados (game days) semestrales
  • SLAs documentados con clientes

Conclusión

El disaster recovery no es un proyecto. Es una práctica continua que escala con tu negocio. Una startup pre-revenue necesita backups automáticos y protección contra borrado accidental. Una startup con clientes enterprise necesita replicación cross-region y failover automatizado.

El error más peligroso es no hacer nada. El segundo más peligroso es configurar backups y no probar la restauración. El coste de una estrategia de DR básica (Nivel 1-2) es inferior a 200 dolares al mes. El coste de perder los datos de tus clientes es la empresa entera.

Si necesitas ayuda para diseñar una estrategia de DR adecuada a tu etapa de crecimiento, nuestro equipo de consultoría AWS implementa estas estrategias regularmente. Solicita una auditoría gratuita para evaluar tu nivel actual de protección.

Back to Blog

Related Posts

View All Posts »