
RTO y RPO en aplicaciones web: guía de diseño para recuperación
Casi todos los equipos pueden citar una cifra de disponibilidad, pero muchos menos saben cuántos datos perderían o cuánto tiempo estarían caídos si su base de datos principal desapareciera esta tarde. Esos dos números, el objetivo de punto de recuperación (RPO) y el objetivo de tiempo de recuperación (RTO), son el núcleo técnico de cualquier plan de continuidad. Esta guía explica cómo definir RTO y RPO en aplicaciones web componente por componente, y cómo diseñar los respaldos, la replicación, la conmutación por error y los modos degradados que de verdad los cumplen. Si primero necesitas el lado de negocio (procesos críticos, personas, comunicación), empieza por nuestro plan de continuidad del negocio para pequeñas empresas en 8 pasos. Este artículo trata del sistema que hay debajo.
Qué miden realmente el RTO y el RPO
- RPO es la cantidad máxima de datos, medida en tiempo, que aceptas perder. Un RPO de 15 minutos significa que, tras la recuperación, pueden faltar como mucho las escrituras de los últimos 15 minutos.
- RTO es el tiempo máximo desde el inicio de un incidente hasta que el servicio vuelve a ser usable. Incluye detección, decisión, restauración y verificación, no solo lo que tarda en correr el comando de restauración.
Dos consecuencias que los ingenieros suelen pasar por alto:
- El RPO es una propiedad de cada almacén de datos, no de la aplicación. Tu clúster de Postgres, tu bucket de S3 con archivos subidos, tu cola en Redis y tu CRM externo tienen cada uno su propio RPO. El RPO real de la aplicación es el peor de ellos para cada flujo de usuario.
- El RTO lo dominan las personas y las dependencias. Una restauración de 20 minutos no sirve de nada si nadie nota la caída durante una hora, o si la aplicación restaurada no arranca porque sus secretos vivían en la cuenta comprometida.
Cómo definir RTO y RPO en aplicaciones web componente por componente
Empieza con un inventario, pero que sea un modelo de datos y no una hoja de cálculo que nadie actualiza. Mantén un catálogo de recuperación en el repositorio, junto a tu código de infraestructura, para que se revise en los pull requests:
# recovery-catalog.yaml
workflows:
checkout:
rto: 1h
rpo: 5m
depends_on: [postgres-main, payments-provider, object-store-invoices]
reporting:
rto: 24h
rpo: 24h
depends_on: [postgres-replica, warehouse]
components:
postgres-main:
kind: database
backup: pitr # respaldo base + archivo de WAL
copy_location: separate-account
restore_runbook: runbooks/postgres-pitr.md
last_restore_test: null # lo llena el job de simulacro de restauración
object-store-invoices:
kind: blob
backup: versioning + cross-account replication
payments-provider:
kind: external
degraded_mode: queue-and-retry
Esta estructura permite responder preguntas concretas de forma automática: qué flujos dependen de un componente que nunca se ha probado restaurando, o qué componente usa un método de respaldo incapaz de cumplir el RPO más estricto de los flujos que lo usan. Un pequeño script en CI que falle cuando los objetivos de rpo sean más estrictos de lo que el método de backup puede entregar es barato y detecta desviaciones.
Define los objetivos por flujo junto con el responsable del negocio, no por servidor. El checkout y el inicio de sesión suelen merecer objetivos estrictos; los reportes internos a menudo pueden esperar un día. Endurecer un objetivo siempre cuesta dinero y complejidad, así que cada número estricto debe justificarse.
Elegir un patrón de recuperación: los trade-offs
El whitepaper de recuperación ante desastres de AWS describe cuatro patrones que sirven como vocabulario en cualquier nube: respaldo y restauración, pilot light, warm standby y multi-sitio activo/activo. Señala que si tu definición de desastre incluye perder una región completa, o si la regulación lo exige, deberías considerar pilot light, warm standby o multi-sitio activo/activo.
- Respaldo y restauración. El más barato y el más lento. Reconstruyes la infraestructura desde código y restauras los datos. El RTO depende de lo rápido que puedas aprovisionar y del tamaño de tus datos. Suficiente para la mayoría de herramientas internas y muchos SaaS pequeños, siempre que la infraestructura esté totalmente codificada.
- Pilot light. Los datos se replican continuamente al sitio de recuperación, pero el cómputo está apagado o al mínimo. Conmutar significa arrancar y escalar los servidores de aplicación. Buen punto medio cuando el RPO debe ser estricto pero el RTO puede ser de decenas de minutos.
- Warm standby. Una copia reducida pero totalmente funcional corre todo el tiempo. Puedes enviarle tráfico de prueba, lo que hace mucho más fácil confiar en ella. La conmutación consiste sobre todo en escalar y enrutar tráfico.
- Multi-sitio activo/activo. Ambos sitios atienden tráfico. El RTO más bajo, pero te obliga a resolver conflictos de escritura, residencia de datos y latencia entre regiones. Cambia tu modelo de datos, no solo tu infraestructura.
El trade-off honesto: cada escalón reduce el RTO, pero también aumenta la cantidad de piezas que pueden fallar en la operación normal. Para muchos equipos pequeños, un respaldo y restauración bien probado con infraestructura como código supera a un warm standby mal probado.
La base de datos es donde se decide el RPO
En PostgreSQL, un pg_dump nocturno te da un RPO de hasta un día. Para algo más estricto, usa archivado continuo y recuperación a un punto en el tiempo (PITR): un respaldo base más el flujo de archivos del write-ahead log (WAL). La documentación de PostgreSQL sobre PITR es la referencia, y un detalle importa para el RPO: el comando de archivado solo se invoca sobre segmentos de WAL completos. En una base con poca actividad un segmento puede tardar mucho en llenarse, así que configura archive_timeout para forzar cambios regulares:
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /mnt/wal_archive/%f && cp %p /mnt/wal_archive/%f'
archive_timeout = 300 # forzar un cambio de segmento al menos cada 5 minutos
En producción, reemplaza el cp por una herramienta que envíe el WAL a almacenamiento de objetos en otra cuenta (pgBackRest y WAL-G son opciones comunes) y alerta cuando el archivado falle. Cada cambio forzado sigue produciendo un archivo de segmento de tamaño completo, así que timeouts muy cortos aumentan el uso de almacenamiento.
Para recuperar hasta un momento justo antes de un error destructivo, restauras el respaldo base, defines un objetivo y, según la documentación, creas un archivo recovery.signal en el directorio de datos antes de arrancar el servidor:
restore_command = 'cp /mnt/wal_archive/%f %p'
recovery_target_time = '2025-01-15 14:02:00+00'
recovery_target_action = 'pause'
Pausar te permite inspeccionar los datos antes de promover. La misma documentación sugiere restringir temporalmente pg_hba.conf para que los usuarios normales no se conecten hasta estar seguro de que la recuperación salió bien.
La replicación no es un respaldo. Una réplica en streaming reduce el RTO ante fallos de hardware o de zona, pero reproduce fielmente un DROP TABLE, una migración con errores o un cifrado por ransomware. Necesitas ambas cosas: réplicas para disponibilidad y PITR para recuperabilidad. Las bases de datos gestionadas (RDS, Cloud SQL, Azure Database) ofrecen PITR como una opción, pero revisa la ventana de retención y si los snapshots se pueden copiar fuera de la cuenta.
Todo lo que no es la base de datos
- Almacenamiento de objetos. Activa el versionado para que sobrescrituras y borrados sean recuperables, y replica a un bucket en una cuenta separada con credenciales propias.
- Colas y cachés. Decide explícitamente si son estado o no. Si una cola en Redis guarda trabajos que no existen en ningún otro lugar, tiene un RPO y necesita persistencia o una fuente de verdad durable. Prefiere diseños en los que la cola pueda reconstruirse desde la base de datos.
- Secretos y configuración. Tu entorno de recuperación necesita credenciales de DNS, certificados TLS, llaves de API y las llaves para descifrar los respaldos. Guarda una copia sellada fuera de la cuenta principal, o descubrirás durante el incidente que las llaves de tus respaldos estaban dentro del radio de impacto.
- Infraestructura. Si producción se creó a mano en una consola, tu RTO es lo que tarde alguien en recordar cada clic. Terraform, Pulumi o CloudFormation convierten la reconstrucción en un paso revisado y repetible.
- SaaS de terceros. Los proveedores de email, las pasarelas de pago y los CRM tendrán caídas que no puedes arreglar. Exporta lo que puedas de forma programada y diseña la aplicación para degradarse en lugar de fallar.
Diseñar para el modo degradado
Cuando falla una dependencia, el objetivo es mantener vivo el flujo crítico con funciones reducidas. Dos patrones hacen la mayor parte del trabajo.
Circuit breaker. Deja de llamar durante un tiempo a una dependencia que falla, para que las peticiones fallen rápido en lugar de acumularse esperando timeouts:
class CircuitBreaker {
private failures = 0;
private openedAt = 0;
constructor(private threshold = 5, private cooldownMs = 30_000) {}
async call<T>(fn: () => Promise<T>, fallback: () => T): Promise<T> {
const open = this.failures >= this.threshold &&
Date.now() - this.openedAt < this.cooldownMs;
if (open) return fallback();
try {
const result = await fn();
this.failures = 0;
return result;
} catch (err) {
this.failures++;
if (this.failures >= this.threshold) this.openedAt = Date.now();
return fallback();
}
}
}
Outbox transaccional. En lugar de llamar al proveedor de email o de webhooks dentro de la petición, escribe la intención en tu propia base de datos en la misma transacción que el cambio de negocio, y deja que un worker la entregue con reintentos:
BEGIN;
INSERT INTO orders (id, customer_id, total) VALUES ($1, $2, $3);
INSERT INTO outbox (id, topic, payload, status)
VALUES (gen_random_uuid(), 'order.confirmation_email',
jsonb_build_object('order_id', $1), 'pending');
COMMIT;
Si el proveedor de email cae durante dos horas, los pedidos se siguen registrando y las confirmaciones salen cuando vuelva. Haz el worker idempotente, porque los reintentos entregarán algunos mensajes más de una vez.
Trata las restauraciones como un job programado, no como una esperanza
Un respaldo que nunca has restaurado es una hipótesis. Automatiza un simulacro de restauración que corra de forma programada, restaure el último respaldo en un entorno aislado, ejecute comprobaciones básicas y registre cuánto tardó:
#!/usr/bin/env bash
set -euo pipefail
start=$(date +%s)
./scripts/provision-restore-env.sh # infra como código, red aislada
./scripts/restore-latest.sh --target restore-env
psql "$RESTORE_URL" -c "SELECT count(*) FROM orders WHERE created_at > now() - interval '1 day';"
./scripts/smoke-tests.sh "$RESTORE_APP_URL"
end=$(date +%s)
echo "restore_seconds=$((end-start))" | ./scripts/report-metric.sh
./scripts/destroy-restore-env.sh
Compara el tiempo de restauración medido y la antigüedad de la fila más reciente restaurada con el RTO y el RPO de tu catálogo, y actualiza last_restore_test. Después, al menos de vez en cuando, haz un game day completo en el que alguien que no escribió el runbook lo siga.
Errores comunes
- Respaldos guardados en la misma cuenta, con las mismas credenciales, que producción.
- Solo dumps nocturnos mientras el negocio asume que no habrá pérdida de datos.
- Una réplica tratada como estrategia de respaldo.
- El archivado de WAL fallando en silencio durante semanas porque nadie tiene alertas.
- Runbooks de restauración que dependen del propio sistema que se restaura (por ejemplo, una wiki alojada en el mismo clúster).
- Medir el RTO como la duración de la restauración, ignorando el tiempo de detección y de decisión.
- Llamadas síncronas a proveedores de email, SMS o CRM dentro de peticiones críticas.
Checklist
- Catálogo de recuperación en el repositorio con RTO y RPO por flujo y dependencias por componente.
- Cada componente con estado tiene un método de respaldo capaz de cumplir su RPO más estricto.
- PITR activado en la base de datos, con
archive_timeouto equivalente ajustado a tu RPO, y alertas ante fallos de archivado. - Copias de respaldo en una cuenta separada con credenciales separadas; almacenamiento de objetos versionado.
- Secretos, acceso al DNS y llaves de descifrado recuperables fuera de la cuenta principal.
- Toda la infraestructura definida como código y reconstruible desde cero.
- Circuit breakers y un outbox alrededor de los proveedores externos en rutas críticas.
- Simulacros de restauración automatizados con tiempos medidos, más game days periódicos con personas.
Hacer esto bien depende sobre todo de un diseño cuidadoso, no de herramientas caras, y es mucho más fácil incorporarlo desde el inicio que añadirlo después. Si estás planeando un sistema nuevo o reforzando uno existente, nuestro equipo desarrolla software a la medida diseñado para recuperarse desde el primer día.
Preguntas frecuentes
¿Cuál es la diferencia entre RTO y RPO?
El RPO es cuántos datos recientes puedes permitirte perder, expresado en tiempo. El RTO es cuánto tiempo puede estar el servicio no disponible. Un sistema puede tener un RPO estricto (archivado continuo de WAL) y aun así un RTO largo (reconstrucción manual), así que debes diseñar para ambos por separado.
¿Basta con una réplica de lectura para la recuperación ante desastres?
No. Una réplica te ayuda a recuperarte rápido de un fallo de servidor o de zona, pero copia los cambios destructivos de inmediato. Sigues necesitando respaldos a un punto en el tiempo guardados fuera de la cuenta principal.
¿Con qué frecuencia debemos probar las restauraciones?
Con la suficiente para descubrir un respaldo roto antes de necesitarlo. Automatizar el simulacro como un job programado abarata las pruebas frecuentes; ajusta la frecuencia a qué tan seguido cambian tu esquema, tu volumen de datos y tu infraestructura.


