Ir al contenido

Automatizar backups incrementales de PostgreSQL en Docker con verificación y rotación

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
#

Tenía PostgreSQL corriendo en Docker y los backups manuales no escalan. Necesitaba algo automático, que verificara que los datos estuvieran bien y que limpiara lo viejo sin intervención.

La solución que implementé
#

Creé un script bash que:

  • Genera backups incrementales en formato custom (comprimido)
  • Verifica la integridad de cada copia
  • Mantiene solo los últimos N backups
  • Corre vía cron en horarios específicos

Paso 1: Preparar el almacenamiento externo
#

Monté un disco externo en /mnt/backups-postgres/:

sudo mkdir -p /mnt/backups-postgres
sudo chown $USER:$USER /mnt/backups-postgres
chmod 700 /mnt/backups-postgres

Cada día un subdirectorio separado:

mkdir -p /mnt/backups-postgres/$(date +%Y-%m-%d)

Paso 2: El script de backup
#

Creé /home/rogelio/scripts/backup-postgres.sh:

#!/bin/bash

BACKUP_DIR="/mnt/backups-postgres/$(date +%Y-%m-%d)"
CONTAINER_NAME="postgres-prod"
DB_USER="postgres"
TIMESTAMP=$(date +%H-%M-%S)
BACKUP_FILE="$BACKUP_DIR/backup-$TIMESTAMP.custom"
LOG_FILE="$BACKUP_DIR/backup-$TIMESTAMP.log"

# Crear directorio del día
mkdir -p "$BACKUP_DIR"

# Ejecutar dump incrementa (custom format)
docker exec $CONTAINER_NAME pg_dump \
  -U $DB_USER \
  -Fc \
  --verbose \
  --compress=9 \
  postgres > "$BACKUP_FILE" 2> "$LOG_FILE"

if [ $? -eq 0 ]; then
  echo "✓ Backup exitoso: $BACKUP_FILE" >> "$LOG_FILE"
  
  # Verificación de integridad
  docker exec $CONTAINER_NAME pg_restore \
    -U $DB_USER \
    --list "$BACKUP_FILE" > /dev/null 2>&1
  
  if [ $? -eq 0 ]; then
    echo "✓ Verificación OK" >> "$LOG_FILE"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - Backup exitoso y verificado" >> "$BACKUP_DIR/resumen.log"
  else
    echo "✗ Verificación fallida" >> "$LOG_FILE"
    exit 1
  fi
else
  echo "✗ Error en backup" >> "$LOG_FILE"
  exit 1
fi

Paso 3: Rotación de backups antiguos
#

Agregué al script una función de limpieza:

# Mantener solo 7 días de backups
RETENTION_DAYS=7
find /mnt/backups-postgres -maxdepth 1 -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \; 2>/dev/null

echo "Rotación completada: directorios mayores a $RETENTION_DAYS días eliminados"

Paso 4: Automatizar con cron
#

crontab -e

Agregué estas líneas:

# Backup cada 6 horas
0 0,6,12,18 * * * /home/rogelio/scripts/backup-postgres.sh

# Verificación semanal de integridad
0 3 * * 0 /home/rogelio/scripts/verify-backups.sh

Paso 5: Script de verificación adicional
#

Creé /home/rogelio/scripts/verify-backups.sh para validar todos los backups de una vez:

#!/bin/bash

BACKUP_DIR="/mnt/backups-postgres"
FAILED=0

for backup_file in $(find $BACKUP_DIR -name "*.custom" -type f); do
  docker exec postgres-prod pg_restore \
    -U postgres \
    --list "$backup_file" > /dev/null 2>&1
  
  if [ $? -ne 0 ]; then
    echo "✗ Fallo verificación: $backup_file"
    FAILED=$((FAILED + 1))
  fi
done

if [ $FAILED -eq 0 ]; then
  echo "✓ Todos los backups verificados correctamente"
else
  echo "✗ $FAILED backups fallaron verificación"
  # Aquí podrías enviar alerta por correo
  exit 1
fi

Monitoreo real
#

Los logs quedan en cada directorio. Reviso el resumen:

tail -f /mnt/backups-postgres/$(date +%Y-%m-%d)/resumen.log

Lo que aprendí
#

  • El formato custom es mejor que SQL plano: comprime más y permite restaurar tablas específicas
  • Validar con pg_restore --list es rápido y confiable
  • No confíes solo en el cron: agrega logs para verificar que corre
  • El almacenamiento externo montado directamente es más rápido que NAS

Llevo 6 meses con esto sin problemas. Los backups incrementales con rotación automática funcionan sin intervención y la verificación de integridad te da tranquilidad real.