Ir al contenido

Recuperación ante desastres: restore completo de Docker y PostgreSQL desde Restic sin downtime

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 escenario
#

Se me quemó la placa madre del servidor doméstico. Tenía todo en Docker: aplicaciones, PostgreSQL, datos críticos. El backup estaba en Restic, pero nunca había probado la recuperación completa en un servidor nuevo. Aquí documento cómo lo hice sin perder servicios ni datos.

Preparación inicial en el servidor nuevo
#

Primero, instalé Docker y las herramientas necesarias:

sudo apt-get update
sudo apt-get install -y docker.io docker-compose git curl restic
sudo usermod -aG docker $USER

Creé la estructura de directorios donde vivirán los datos:

mkdir -p /srv/docker-data/{postgres,volumes,configs}
mkdir -p /var/backups/restic-cache

Restaurar la configuración de Restic
#

Lo crítico aquí es configurar Restic con los mismos parámetros que usé para hacer backup. Necesitaba:

  • La contraseña del repositorio
  • Las credenciales del almacenamiento (S3, B2, o dónde esté alojado)
  • La ruta del repositorio

En mi caso usaba B2:

export RESTIC_REPOSITORY="b2:mi-bucket:/ruta/del/repo"
export RESTIC_PASSWORD="mi-contraseña-segura"
export B2_ACCOUNT_ID="id"
export B2_ACCOUNT_KEY="key"

# Verificar que Restic puede acceder al repositorio
restic snapshots

Si ves el listado de snapshots, estás conectado correctamente.

Estrategia de restore sin downtime
#

La clave es no restaurar todo de una vez. Mi plan fue:

  1. Restaurar datos de PostgreSQL en un volumen temporal
  2. Restaurar configs y docker-compose
  3. Levantar servicios no-críticos primero
  4. Restaurar PostgreSQL y validar
  5. Levantar aplicaciones críticas

Restauración del snapshot
#

Primero, listaba los snapshots disponibles:

restic snapshots --json | jq '.[] | {time, id, hostname}'

Elegía el más reciente y lo restauraba a un directorio temporal:

restic restore [SNAPSHOT_ID] --target /tmp/restic-restore

Este proceso tardó unos minutos dependiendo del tamaño. En mi caso, unos 150GB.

Recuperando PostgreSQL
#

Una vez restaurado, localizaba el directorio de datos de PostgreSQL:

ls -la /tmp/restic-restore/srv/docker-data/postgres/data/

Aquí está el problema: no puedo simplemente copiar datos vivos de PostgreSQL. Necesitaba hacer esto correctamente:

# 1. Copiar el directorio de datos restaurado
sudo cp -r /tmp/restic-restore/srv/docker-data/postgres /srv/docker-data/postgres-restored

# 2. Ajustar permisos (PostgreSQL en Docker usa UID 999)
sudo chown -R 999:999 /srv/docker-data/postgres-restored

# 3. Crear volumen temporal para verificar integridad
docker volume create postgres-restore-check
docker run --rm -v postgres-restore-check:/var/lib/postgresql/data -v /srv/docker-data/postgres-restored:/backup postgres:15 \
  /bin/bash -c "pg_controldata /backup/data" 2>&1 | head -20

Si pg_controldata no lanza errores críticos, el backup es válido.

Levantar servicios de forma gradual
#

En lugar de hacer docker-compose up, levantaba servicios individuales:

# Restaurar docker-compose.yml y configs
cp -r /tmp/restic-restore/srv/docker-data/configs /srv/docker-data/

# Levantar servicios sin dependencia de DB primero
docker-compose -f /srv/docker-data/docker-compose.yml up -d redis nginx

# Esperar 30 segundos
sleep 30

# Levantar PostgreSQL
docker-compose up -d postgres

# Verificar logs
docker logs -f [postgres-container-id]

Validación y corte final
#

Antes de considerar la recuperación completa:

# Verificar que PostgreSQL está sano
docker exec -it [postgres-container-id] pg_isready

# Testear una aplicación crítica
curl -s http://localhost:8080/health | jq .

# Revisar espacio en disco
df -h /srv/docker-data

Una vez validado, levantaba el resto de servicios:

docker-compose up -d

Lecciones aprendidas
#

  • Prueba tus backups regularmente. Descubrí un problema de permisos recién al restaurar.
  • Documenta las variables de entorno. Me tomó 15 minutos encontrar las credenciales de B2.
  • Restaura a directorios temporales primero. Así no sobrescribos nada en caso de error.
  • Valida integridad de datos antes de declarar éxito. Los logs de PostgreSQL son tus amigos.

El downtime real fue 0, porque servicios no críticos estuvieron activos mientras restauraba la BD.