# Backups y continuidad (Fase 6) ## 0. Validar antes de diseñar el resto Coolify despliega Postgres como un recurso "managed" que en realidad es un contenedor `postgres:16` estándar con volumen persistente. **Hay que confirmar, para el ambiente real, si se puede:** 1. Montar un `postgresql.conf` custom (para `archive_mode`, `archive_command`, `wal_level`), y 2. Ejecutar un sidecar/proceso adicional (`pgbackrest`) con acceso al mismo volumen de datos y a WAL. - **Si sí se puede**: seguir esta guía con pgBackRest (PITR real). - **Si no se puede** (Coolify no expone esos hooks en el plan/versión usada): el respaldo queda limitado a los `pg_dump` programados que Coolify ya ofrece -- el RPO pasa a ser "la frecuencia del dump", no segundos. En ese caso, para producción, evaluar auto-hospedar Postgres (contenedor propio, fuera del recurso "managed" de Coolify) solo para tener control de WAL -- es la única forma de bajar el RPO por debajo de la frecuencia del dump. Esta decisión condiciona todo lo demás; no asumir que WAL archiving va a funcionar sin probarlo primero contra el Coolify real del ambiente. ## 1. Qué se respalda y con qué política | Qué | Herramienta | Frecuencia | Retención | RPO objetivo | |---|---|---|---|---| | `panels_platform` (prod) | pgBackRest (o `pg_dump` si no hay WAL) | full diario + diferencial c/6h + WAL continuo | 30 días | segundos (con WAL) / 24h (solo dump) | | `panels_product` (prod) | igual que arriba | igual | 30 días | igual | | `panels_platform`/`panels_product` (staging/dev) | `pg_dump` | full diario | 7 días | 24h | | Archivos (Contabo, Fase 4c) | versionado de objetos del bucket + backup del bucket | continuo (versionado) | igual que la BD | ver nota de coordinación abajo | | Redis | RDB snapshot | al reiniciar/periódico | ninguna (no es fuente de verdad) | N/A -- ver `db/backups/redis-persistence.md` | **Respaldar SIEMPRE ambas bases.** Es fácil configurar el backup de `panels_product` (donde está "todo el negocio") y olvidar `panels_platform` (que tiene el registro de TODOS los tenants) -- el día del incidente ahí es cuando se descubre. ## 2. pgBackRest (si Coolify lo permite) Ver [`pgbackrest-panels_platform.conf`](./pgbackrest-panels_platform.conf) y [`pgbackrest-panels_product.conf`](./pgbackrest-panels_product.conf) -- plantillas, una `stanza` por base de datos (no por esquema: `panels_product` es una sola stanza aunque tenga los esquemas iam/core adentro). Pasos (por base): ```bash # 1. postgresql.conf del contenedor wal_level = replica archive_mode = on archive_command = 'pgbackrest --stanza=panels_platform archive-push %p' archive_timeout = 60 # cap de RPO a 60s incluso en bases con poca escritura # 2. Crear la stanza una vez pgbackrest --stanza=panels_platform --log-level-console=info stanza-create # 3. Verificar que el archiving realmente funciona ANTES de confiar en él pgbackrest --stanza=panels_platform check # 4. Backups programados (cron / scheduler de Coolify) pgbackrest --stanza=panels_platform --type=full backup # 1x/semana pgbackrest --stanza=panels_platform --type=diff backup # cada 6h ``` Repetir con `--stanza=panels_product` y su propio `archive_command`. ### Point-in-time recovery ```bash pgbackrest --stanza=panels_platform --type=time \ --target="2026-09-01 14:00:00-06" --target-action=promote restore ``` ## 3. Si NO hay WAL archiving (solo pg_dump) ```bash pg_dump --format=custom --file=panels_platform-$(date +%Y%m%d).dump "$DATABASE_URL_PLATFORM_OWNER" pg_dump --format=custom --file=panels_product-$(date +%Y%m%d).dump "$DATABASE_URL_CORE_OWNER" ``` `--format=custom` permite restore paralelo (`pg_restore -j4`) y restore selectivo por tabla si algún día hace falta. Subir los `.dump` a un bucket S3-compatible (puede ser el mismo de Contabo, con prefijo/bucket distinto al de documentos) con retención por lifecycle policy del bucket. ## 4. Coordinación con el respaldo de archivos (Fase 4c) Un restore de Postgres a un punto en el tiempo, sin restaurar los archivos de Contabo al MISMO punto, deja filas (`documents.storage_name`) apuntando a objetos que ya no existen o que cambiaron. Política: - Activar versionado de objetos en el bucket de Contabo. - Si se hace un PITR de Postgres a una hora `T`, documentar que los archivos subidos/borrados después de `T` pueden quedar huérfanos o inconsistentes -- es una ventana de inconsistencia aceptada, no un bug a corregir en caliente durante el incidente. ## 5. Redis Ver [`redis-persistence.md`](./redis-persistence.md). Resumen: solo RDB para evitar deslogueo masivo, sin retención de negocio, fuera del alcance del simulacro de restauración de abajo. ## 6. Simulacro de restauración (mensual, obligatorio) Ver [`restore-drill-checklist.md`](./restore-drill-checklist.md). Un backup que nunca se probó restaurar no es un backup, es una esperanza.