Problema
En un homelab típico se ejecutan varios motores de bases de datos (PostgreSQL, MySQL, MongoDB, etc.) y contenedores que gestionan volúmenes críticos (archivos de configuración, datos de aplicaciones, backups de máquinas virtuales). Mantener copias de seguridad consistentes, con retención flexible y almacenamiento fuera del nodo, suele ser un dolor de cabeza. Los síntomas más comunes son:
- Pérdida de datos después de una actualización del contenedor.
- Backups que fallan silenciosamente porque la credencial del bucket expiró.
- Retención inadecuada que llena el disco local en pocos días.
- Falta de un proceso de restauración probado, lo que convierte cualquier desastre en tiempo de inactividad prolongado.
El reto no es solo generar el dump, sino orquestar la tarea, almacenar los artefactos de forma segura y validar que la restauración funciona bajo cualquier runtime (Docker, Podman, UnraidOS).
Causa
-
Configuración estática de credenciales
Variables de entorno con claves de acceso a S3 o Azure se guardan en archivos de texto plano dentro del repositorio. Cuando el token expira, el job de backup se detiene sin generar logs claros. -
Políticas de retención mal definidas
Usar “keep all” o “retain 30 days” sin considerar el tamaño de los dumps lleva rápidamente a un llenado del disco y a fallos de escritura. -
Incompatibilidad entre runtimes
Un contenedor preparado para Docker puede fallar en Podman por diferencias en los volúmenes montados o en la gestión de permisos de usuario. -
Ausencia de pruebas de restauración
La mayoría de los equipos confían en que el backup “funciona” porque el proceso de dump no muestra errores. Sin una restauración periódica, los dumps corruptos pasan desapercibidos. -
Dependencia de un único punto de almacenamiento
Guardar los archivos solo en el nodo local elimina la ventaja de la recuperación ante desastres; cualquier fallo del hardware implica pérdida total.
Solución
Una arquitectura modular basada en contenedores permite separar la generación del backup, el transporte a storage y la gestión de retención. Los pasos clave son:
-
Crear un contenedor de backup genérico
Utilizar una imagen ligera (alpine + clientes de S3, Azure CLI, gsutil) que ejecute scripts para cada motor de base de datos. Cada script debe:- Detener brevemente la escritura (por ejemplo,
pg_dumpcon--no-syncomysqldump --single-transaction). - Comprimir el dump (
gzipozstd). - Añadir metadatos de timestamp y nombre del proyecto.
- Detener brevemente la escritura (por ejemplo,
-
Externalizar credenciales
Almacenar claves en un secret manager (Docker secrets, HashiCorp Vault o archivos montados con permisos 0600). El contenedor lee la variableSTORAGE_URLy el token desde/run/secrets. -
Definir políticas de retención con GFS (Grandfather‑Father‑Son)
Un pequeño script en el contenedor evalúa la edad de los archivos y elimina los que no encajan en la regla:- Diarios → 7 días
- Semanales → 4 semanas
- Mensuales → 12 meses
- Anuales → 3 años
-
Orquestar la ejecución con Docker Compose o Kubernetes CronJob
Cada backup se declara como un serviciorestart: "no"y se programa concrondentro del contenedor o con la capa de orquestación. La definición incluye:- Volumenes de origen (
/var/lib/postgresql/data,/var/lib/mysql,/data/app). - Volumen de salida (
/backups) montado en un host o en un contenedor de storage (minio, rclone).
- Volumenes de origen (
-
Implementar pruebas de restauración automáticas
Un job separado extrae el último backup, lo descomprime y ejecuta unrestore --dry-run. Si la prueba falla, se dispara una alerta (Discord, Slack, Telegram). -
Monitoreo y alertas
Exportar métricas de éxito/fallo a Prometheus y crear reglas de alerta para:- Backup fallido > 1 vez.
- Retención excede 80 % del disco.
- Credenciales caducadas.
Cuándo aplicar esta solución
- Entornos con más de una base de datos: la arquitectura modular permite añadir scripts sin tocar la lógica central.
- Uso de varios runtimes: al encapsular la lógica en un contenedor, la misma imagen funciona tanto en Docker como en Podman.
- Necesidad de retención a largo plazo: GFS integrado evita el crecimiento descontrolado.
- Políticas de seguridad estrictas: secret manager y auditoría de logs cumplen con requisitos de compliance.
No es adecuada cuando:
- Solo se necesita un backup puntual y manual; la sobrecarga de orquestación resulta innecesaria.
- El hardware no soporta contenedores (por ejemplo, routers con firmware limitado).
Código
version: "3.9"
services:
backup:
image: alpine:3.20
container_name: homelab-backup
restart: "no"
environment:
- STORAGE_URL=s3://my-backup-bucket
secrets:
- s3_access_key
- s3_secret_key
volumes:
- pg_data:/var/lib/postgresql/data:ro
- mysql_data:/var/lib/mysql:ro
- app_vol:/data/app:ro
- backups:/backups
command: >
/bin/sh -c "
/scripts/pg_dump.sh &&
/scripts/mysql_dump.sh &&
/scripts/app_vol_backup.sh &&
/scripts/retention_gfs.sh
"
minio:
image: minio/minio
container_name: minio
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
command: server /data
ports:
- "9000:9000"
volumes:
- minio_data:/data
secrets:
s3_access_key:
file: ./secrets/s3_access_key.txt
s3_secret_key:
file: ./secrets/s3_secret_key.txt
volumes:
pg_data:
external: true
mysql_data:
external: true
app_vol:
external: true
backups:
driver: local
minio_data:
Verificación
- Comprobar la existencia del archivo
ls -lh backups/$(date +%Y-%m-%d)*.gz - Validar integridad
gunzip -c backups/$(date +%Y-%m-%d)_pg.dump.gz | pg_restore --list - Ejecutar restauración de prueba
docker exec -i postgres_container pg_restore -U postgres -d testdb < <(gunzip -c backups/$(date +%Y-%m-%d)_pg.dump.gz) - Revisar métricas
Acceder ahttp://localhost:9090/graphy buscar la seriebackup_success_total.
Si los pasos anteriores generan resultados sin errores, la cadena de backup está operativa.
Notas adicionales
- Rotación de credenciales: programa una rotación mensual y actualiza los secrets con
docker secret rmydocker secret create. - Compatibilidad con Podman: reemplaza
dockerporpodmanen los comandos; la sintaxis del compose es idéntica si usaspodman-compose. - Almacenamiento en Azure o GCS: solo cambia la variable
STORAGE_URLaaz://containerogs://buckety asegura que el contenedor tengaazcopyogsutilinstalados. - Escalado: para grandes volúmenes, considera dividir los dumps por tabla y paralelizar la compresión con
pigz. - Auditoría: habilita
--log-fileen cada script y envía los logs a un syslog central para cumplir con requisitos de auditoría.