Miércoles por la mañana. 09:15. Un corte de energía inesperado. El sistema de alimentación ininterrumpida (UPS) debería haber cubierto la carga, pero algo salió mal. No resistió. Resultado: apagado repentino de los servidores. Todos ellos. Incluido el servidor de base de datos PostgreSQL que gestionaba 8 años de datos de facturación de una empresa manufacturera con 200 empleados. La copia de seguridad más reciente tenía 48 horas. El peor escenario para cualquier sysadmin o DBA: una base de datos crítica corrupta en producción. La presión era palpable. La facturación paralizada significaba problemas de liquidez inminentes. Este es el relato de cómo gestionamos la recuperación, desde los errores iniciales hasta las decisiones que salvaron lo salvable, y las lecciones aprendidas para evitar que la historia se repita.
Requisitos Previos / Entorno de Prueba
El incidente ocurrió en un servidor Debian 12 que alojaba PostgreSQL 16. La base de datos era de aproximadamente 500 GB. El sistema de copias de seguridad consistía en pg_basebackup diario y un archive_command para los WALs, configurado para ejecutarse cada 15 minutos en un almacenamiento NFS. No había streaming replication configurada. La lección aprendida aquí es que incluso con un entorno que parece robusto, la ausencia de una estrategia de recuperación de punto en el tiempo (PITR) bien ajustada y de una réplica en caliente puede ser catastrófica.
El crash: PostgreSQL no arranca después de un corte de energía
El corte de energía causó un apagado ‘sucio’ del servidor que albergaba la base de datos PostgreSQL. Al reiniciar, el servicio no se inició. Los intentos de arrancar PostgreSQL resultaron en errores en el log, indicando una corrupción del estado interno. Estaba claro: el clúster ya no era consistente y no podía reanudar las operaciones normales. Un momento de pánico inicial, seguido de la conciencia de que cada acción debía ser medida, rápida y documentada.
Horas 0-2: diagnóstico — errores en el WAL y checkpoint corrupto
La primera acción es siempre la misma: consultar los logs. La verbosidad de PostgreSQL es una aliada en estos momentos. Encontramos mensajes que indicaban la corrupción de los archivos WAL (Write-Ahead Log) y la imposibilidad de restaurar un checkpoint válido. Este es un síntoma clásico de un crash repentino que no permitió a la base de datos escribir todas las transacciones pendientes en los discos y actualizar correctamente los metadatos del checkpoint.
Para diagnosticar el estado del clúster y verificar los logs, utilicé:
# Comprobar estado del clúster PostgreSQL
pg_lsclusters
# Verificar log de errores
tail -100 /var/log/postgresql/postgresql-*.log
Los logs confirmaron que el problema residía en la integridad del WAL y en el checkpoint. Esto significaba que la base de datos no podía determinar desde qué punto reiniciar de forma segura para garantizar la consistencia de los datos.
La primera mala elección que casi empeora todo
Bajo presión, la tentación de buscar soluciones rápidas es enorme. Uno de los primeros comandos que viene a la mente de quien tiene experiencia con PostgreSQL en situaciones de emergencia es pg_resetwal. Este comando está diseñado para ‘resetear’ el estado de los WAL, haciendo creer a la base de datos que no hay transacciones pendientes por recuperar. Casi lo ejecuté. Afortunadamente, un colega más cauto me detuvo. pg_resetwal puede permitir que la base de datos se reinicie, pero lo hace ignorando el estado de los WAL, lo que puede llevar a una pérdida significativa de datos o a una inconsistencia silenciosa. Es una herramienta potente, pero solo debe usarse cuando se acepta una potencial pérdida de datos o cuando se está absolutamente seguro de que los datos en los WAL no son recuperables o importantes. En este caso, con datos de facturación críticos, no podíamos permitirnos errores.
Recuperación con pg_resetwal: cuándo usarlo y cuándo NO
pg_resetwal (o pg_resetxlog en versiones más antiguas) es una utilidad de emergencia. Su propósito principal es ‘engañar’ a PostgreSQL para que reinicie después de un crash grave que ha corrompido los archivos de control o los WAL de forma irrecuperable. Funciona reescribiendo los metadatos del clúster, como el último checkpoint y la posición del WAL, a valores que permiten que la base de datos se inicie. Sin embargo, no repara los datos corruptos y puede llevar a una base de datos inconsistente. Si se usa de forma incorrecta, puede transformar una corrupción parcial en una pérdida total de datos.
Cuándo usarlo:
- Como última opción, cuando todos los demás intentos de recuperación han fallado.
- Cuando se está dispuesto a aceptar una potencial pérdida de datos o inconsistencias.
- En entornos de prueba o desarrollo donde la pérdida de datos no es crítica.
Cuándo NO usarlo:
- En una base de datos de producción crítica sin una copia de seguridad reciente y válida.
- Si existe la posibilidad de recuperar los datos mediante Point-in-Time Recovery (PITR).
- Sin haber hecho antes una copia completa del clúster corrupto para análisis forense o intentos de recuperación alternativos.
En nuestro caso, decidimos no usar pg_resetwal porque teníamos una copia de seguridad, aunque anticuada, y esperábamos poder recuperar las transacciones más recientes a través de los WAL archivados.
Restauración desde backup + recuperación a un punto en el tiempo (PITR) con archivado de WAL
La estrategia ganadora fue el Point-in-Time Recovery (PITR). Teníamos una copia de seguridad completa de la base de datos de 48 horas antes. Fundamentalmente, también habíamos configurado el archivado de WAL, que copiaba los archivos WAL a una ubicación de almacenamiento separada y segura. Esto nos permitió restaurar la base de datos al estado de la copia de seguridad y luego ‘reproducir’ todas las transacciones contenidas en los archivos WAL hasta un punto específico antes del crash.
Los pasos fueron los siguientes:
- Copia de seguridad del clúster corrupto: Antes de cualquier operación, hicimos una copia completa del directorio de datos de PostgreSQL. Este es un paso crítico para tener un punto de restauración en caso de errores adicionales.
- Restauración del backup base: Restauramos la copia de seguridad completa de la base de datos en un nuevo directorio de datos.
- Configuración de
recovery.conf(opostgresql.confen versiones recientes): Modificamos el archivo de configuración para indicar la ubicación de los WAL archivados y el punto temporal deseado para la recuperación.
# postgresql.conf (o recovery.conf en versiones anteriores)
restore_command = 'cp /backup/wal/%f %p' # Asegúrate de que la ruta sea correcta
recovery_target_time = '2026-06-10 09:45:00' # La hora exacta del crash o poco antes
- Inicio de la base de datos en modo recuperación: Al reiniciar, PostgreSQL leyó la copia de seguridad, luego comenzó a aplicar los WAL archivados secuencialmente hasta alcanzar
recovery_target_time. Este proceso reconstruyó el estado de la base de datos hasta pocos minutos antes del corte de energía.
Una vez completada la recuperación, es fundamental verificar la integridad de los datos. Usé pg_dumpall | grep -c 'CREATE TABLE' para tener una estimación rápida del número de tablas y compararla con las expectativas. Luego, controles más profundos sobre los datos críticos.
# Verificación de consistencia después de la recuperación (ejemplo rápido)
pg_dumpall | grep -c 'CREATE TABLE'
Cuánto perdimos y por qué pudo haber sido cero
La recuperación fue un éxito, pero no sin pérdida. Perdimos aproximadamente 15 minutos de transacciones. Esto se debe a que la frecuencia de archivado de los WAL estaba configurada en 15 minutos. Todas las transacciones que ocurrieron entre el último WAL archivado y el momento del crash se perdieron. Para una empresa que emite facturas continuamente, 15 minutos de datos faltantes son un problema significativo, aunque manejable manualmente reconstruyendo las últimas operaciones.
Esta pérdida pudo haber sido cero si la frecuencia de archivado de los WAL hubiera sido más agresiva (por ejemplo, cada minuto) o, idealmente, si se hubiera configurado una streaming replication con un servidor standby. Esta última opción habría garantizado un RPO (Recovery Point Objective) casi a cero, minimizando la pérdida de datos a unos pocos segundos o incluso cero en caso de failover automático.
Configuración post-incidente: streaming replication y política de backup
El post-mortem de un incidente es el mejor momento para implementar mejoras. Inmediatamente implementamos dos cambios críticos:
- Streaming Replication: Configuramos una réplica síncrona (o asíncrona, según los requisitos de RPO) en un servidor separado. Esto asegura que siempre haya una copia actualizada de la base de datos, lista para tomar el relevo en caso de fallo del primario. Esto reduce drásticamente el RPO y el RTO (Recovery Time Objective).
- Revisión de la Política de Backup: La política de backup fue revisada para incluir no solo backups completos diarios, sino también un archivado continuo de WAL, con una frecuencia de archivado reducida a 1 minuto, y pruebas regulares del proceso de recuperación. No basta con tener un backup; es esencial saber que funciona y cuánto tiempo lleva la recuperación.
Una organización con 2.000 endpoints y un entorno con más de 300 VMs no puede permitirse un RPO de 48 horas. Las lecciones aprendidas en este incidente reforzaron la necesidad de una estrategia robusta de Recuperación ante Desastres y Continuidad del Negocio, no solo en palabras, sino con implementaciones concretas y probadas. Incidentes como este, aunque dolorosos, son oportunidades para fortalecer la infraestructura.
Errores Comunes y Resolución de Problemas
- No tener un backup reciente: Parece trivial, pero es la causa principal de desastres. Backups regulares y probados son innegociables.
- No archivar los WAL: Sin archivado de WAL, el PITR es imposible, y el RPO está ligado solo a la frecuencia de los backups completos.
- No probar la recuperación: Un backup no probado es un backup inexistente. Simule regularmente escenarios de desastre.
- Usar
pg_resetwala ciegas: Evalúe siempre el riesgo de pérdida de datos. Es mejor una recuperación más larga pero segura que una recuperación rápida pero inconsistente. - Ignorar los logs: Los logs de PostgreSQL contienen toda la información para entender qué sucedió y cómo proceder.
Conclusiones con Puntos Clave Operativos
El incidente de corrupción de la base de datos PostgreSQL fue un recordatorio brutal de la importancia de una estrategia de Recuperación ante Desastres bien diseñada y regularmente probada. No basta con tener un backup; es fundamental que ese backup sea válido y que el proceso de recuperación sea eficiente. La lección principal es que el archivado de WAL y, idealmente, la streaming replication, son componentes no negociables para cualquier base de datos PostgreSQL en producción con requisitos de alta disponibilidad y baja pérdida de datos. Un RPO de 15 minutos es un lujo que pocas empresas pueden permitirse. Un RPO cercano a cero es el objetivo, alcanzable con las implementaciones correctas y una cultura de la resiliencia.
Lee también: Lynis Audit Linux: Guía Práctica de Hardening 2026
Para más detalles sobre el Point-in-Time Recovery de PostgreSQL, puedes consultar la documentación oficial: [https://www.postgresql.org/docs/current/backup-recovery.html]