↓ Ir al contenido

Recuperación ante corrupción de PostgreSQL en Docker: pg_dump, verificación y restore selectivo

Rogelio Guerra Riverón
Autor
Rogelio Guerra Riverón
Construyendo mi propia infraestructura web desde cero. Aquí documento cada paso: servidores, redes, contenedores y lo que vaya surgiendo.

El problema real
#

Hace poco mi contenedor PostgreSQL empezó a tirar errores de corrupción en tablas críticas. El error típico: ERROR: invalid page header in block. No podía permitirme downtime, así que tuve que idear una estrategia de recuperación sin afectar la producción.

La solución no es mágica, pero es metódica. Te cuento exactamente qué hice.

Paso 1: Verificar la corrupción
#

Primero, necesitas saber qué está roto. Accede al contenedor:

docker exec -it mi_postgres psql -U usuario -d mi_base_de_datos

Dentro de psql, ejecuta:

REINDEX DATABASE mi_base_de_datos;

Si esto falla con errores de página corrupta, confirmaste el problema. Anota qué tablas afecta.

Paso 2: Hacer un dump limpio (sin la corrupción)
#

Aquí va lo importante: pg_dump puede saltarse las páginas corruptas si usas la opción correcta:

docker exec mi_postgres pg_dump \
  -U usuario \
  -d mi_base_de_datos \
  --no-acl \
  --no-owner \
  -F c \
  > /backup/dump_limpio.dump

El -F c genera un formato comprimido binario. Es más eficiente y te da más control en el restore.

Paso 3: Verificar la integridad del dump
#

No confíes en que todo se copió bien. Valida el archivo:

docker exec mi_postgres pg_restore --list /backup/dump_limpio.dump | head -20

Esto lista el contenido sin restaurar. Revisa que las tablas críticas estén presentes. También puedes verificar el tamaño:

ls -lh /backup/dump_limpio.dump

Si es significativamente más pequeño que esperabas, algo va mal.

Paso 4: Restore selectivo en un contenedor temporal
#

Nunca restauras directamente en producción. Levanta un contenedor de test:

docker run --name pg_test \
  -e POSTGRES_USER=usuario \
  -e POSTGRES_PASSWORD=temporal \
  -e POSTGRES_DB=test_db \
  -d postgres:15

Copia el dump al contenedor:

docker cp /backup/dump_limpio.dump pg_test:/

Restaura:

docker exec pg_test pg_restore \
  -U usuario \
  -d test_db \
  --data-only \
  /dump_limpio.dump

El --data-only omite la creación de esquema. Si tienes esquemas rotos, restauras solo datos en una estructura sana.

Paso 5: Validar datos en el contenedor test
#

Conecta al contenedor test y verifica:

docker exec -it pg_test psql -U usuario -d test_db

Ejecuta queries críticas:

SELECT COUNT(*) FROM tabla_critica;
SELECT * FROM tabla_critica LIMIT 5;
VACUUM ANALYZE;

Si todo anda bien, procede. Si hay problemas, tienes un sandbox donde jugar.

Paso 6: Migración a producción
#

Opción A (si tienes replica o puedes afectar):

docker exec mi_postgres_prod pg_dump \
  -U usuario \
  -d mi_base_de_datos \
  --schema-only \
  > /backup/esquema.sql

Restauras esquema limpio, luego datos desde tu dump verificado.

Opción B (si necesitas zero downtime):

Usas pg_upgrade o replicación física. Sincronizas el contenedor test como replica, promueves y switcheas.

Paso 7: Limpieza y documentación
#

Una vez que producción está estable:

docker stop pg_test
docker rm pg_test

Archiva los dumps:

gzip /backup/dump_limpio.dump

Y documenta qué tablas tuvieron problemas. Esto avisa si hay un patrón de hardware o configuración.

Lecciones aprendidas
#

  • No confíes en un solo backup. Haz dumps regulares.
  • Siempre valida en un entorno de test antes de tocar producción.
  • pg_restore con opciones selectivas es tu aliado. Lee su manual.
  • Si ves corrupción repetida, revisa el filesystem del host. Puede ser síntoma de hardware defectuoso.

Este proceso me salvó horas de downtime. No es complejo, solo requiere disciplina y no apresurarse.