Backup automático de volúmenes Docker con Restic y systemd: guía práctica con retención y alertas
Configura backups cifrados, incrementales y automáticos de todos tus volúmenes Docker usando Restic. Programa las copias con systemd timer, define políticas de retención semanal/mensual y recibe notificaciones por correo si algo falla. Listo para producción en menos de 30 minutos.
Por Equipo Starbyte
Backup automático de volúmenes Docker con Restic y systemd: guía práctica con retención y alertas
Problema real: Tienes varios servicios Docker con volúmenes (bases de datos, configuraciones, archivos subidos por usuarios). Haces backups manuales de vez en cuando, o peor, no los haces. Un fallo de disco, un borrado accidental o un ataque de ransomware te costaría días de trabajo. Necesitas un sistema de backup automático, cifrado, eficiente (incremental) y con alertas, que no dependa de scripts frágiles ni de soluciones de pago.
Este post te guía para montar un sistema de backup con Restic, una herramienta moderna que soporta deduplicación, cifrado y múltiples destinos (local, S3, SFTP, etc.), integrado con los volúmenes Docker y gestionado por timers de systemd.
Requisitos previos
- Servidor Linux con Docker y Docker Compose.
systemdcomo sistema de init (Ubuntu 20.04+, Debian 10+, etc.).resticinstalado (sudo apt install restico binario desde GitHub).- Un destino para los backups:
- Directorio local (ej. disco externo montado).
- Repositorio remoto vía SFTP.
- Bucket S3 compatible (AWS, MinIO, Backblaze B2, etc.).
mailutilsomsmtpconfigurado para enviar correos (opcional, para alertas).
1. Flujo general del sistema
- Un script de backup (
restic-backup.sh) inicia Restic, hace copia de los volúmenes Docker, aplica política de retención y registra el resultado. - Un
systemd serviceejecuta ese script. - Un
systemd timerdispara el servicio a una hora programada (ej. 2:00 AM diario). - Si falla, se envía un correo de alerta (usando
OnFailure).
2. Estructura de directorios y variables
Crea una carpeta para el sistema de backup:
sudo mkdir -p /opt/backups
sudo mkdir -p /opt/backups/scripts
sudo mkdir -p /opt/backups/logs
Define las variables de entorno en un archivo que protegeremos:
sudo nano /opt/backups/restic-env
Contenido de ejemplo para repositorio local:
export RESTIC_REPOSITORY="/mnt/backup-disk/docker-volumes"
export RESTIC_PASSWORD="tu-contraseña-segura-backup"
export BACKUP_SOURCE="/var/lib/docker/volumes"
export RETENTION_DAYS=7
export RETENTION_WEEKS=4
export RETENTION_MONTHS=6
Para S3 sería:
export RESTIC_REPOSITORY="s3:s3.amazonaws.com/mi-bucket/backups"
export AWS_ACCESS_KEY_ID="tu-access-key"
export AWS_SECRET_ACCESS_KEY="tu-secret-key"
export RESTIC_PASSWORD="contraseña-fuerte"
# ... igual BACKUP_SOURCE y retenciones
Protege el archivo:
sudo chmod 600 /opt/backups/restic-env
3. Inicializar el repositorio Restic
Solo se hace una vez. Carga las variables y ejecuta:
source /opt/backups/restic-env
sudo -E restic init
El flag -E preserva las variables de entorno con sudo. Verás un mensaje de éxito.
4. Script de backup (restic-backup.sh)
sudo nano /opt/backups/scripts/restic-backup.sh
Contenido:
#!/bin/bash
set -euo pipefail
# Cargar variables
source /opt/backups/restic-env
LOG_FILE="/opt/backups/logs/backup-$(date +%Y%m%d-%H%M%S).log"
echo "=== Backup iniciado: $(date) ===" | tee "$LOG_FILE"
# Backup con exclusión de algunos directorios si se desea
restic backup "$BACKUP_SOURCE" \
--verbose \
--tag docker-volumes \
--exclude="*.tmp" \
--exclude="*.log" \
2>&1 | tee -a "$LOG_FILE"
echo "=== Retención de snapshots ===" | tee -a "$LOG_FILE"
restic forget \
--keep-daily $RETENTION_DAYS \
--keep-weekly $RETENTION_WEEKS \
--keep-monthly $RETENTION_MONTHS \
--prune \
2>&1 | tee -a "$LOG_FILE"
echo "=== Backup finalizado: $(date) ===" | tee -a "$LOG_FILE"
Hazlo ejecutable:
sudo chmod +x /opt/backups/scripts/restic-backup.sh
Explicación: El script hace backup del directorio de volúmenes de Docker (/var/lib/docker/volumes), que es donde residen los datos persistentes por defecto. Si usas volúmenes con paths personalizados, ajusta BACKUP_SOURCE o añade múltiples rutas.
5. Unidad de systemd para el backup
Crea el archivo de servicio:
sudo nano /etc/systemd/system/docker-volumes-backup.service
[Unit]
Description=Backup de volúmenes Docker con Restic
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/opt/backups/restic-env
ExecStart=/opt/backups/scripts/restic-backup.sh
User=root
Group=root
Ahora el timer que lo ejecutará diariamente a las 2:00 AM:
sudo nano /etc/systemd/system/docker-volumes-backup.timer
[Unit]
Description=Ejecuta backup de volúmenes Docker diario a las 2:00 AM
Requires=docker-volumes-backup.service
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1800
[Install]
WantedBy=timers.target
Nota: RandomizedDelaySec añade hasta 30 min de retraso aleatorio para evitar picos si tienes muchas máquinas.
Activa e inicia el timer:
sudo systemctl daemon-reload
sudo systemctl enable docker-volumes-backup.timer
sudo systemctl start docker-volumes-backup.timer
Verifica el estado:
sudo systemctl status docker-volumes-backup.timer
sudo systemctl list-timers --all
Para probar el servicio manualmente:
sudo systemctl start docker-volumes-backup.service
sudo journalctl -u docker-volumes-backup.service -f
6. Alertas por correo si el backup falla
Añade una unidad de systemd para notificar fallos. Crea el servicio de alerta:
sudo nano /etc/systemd/system/docker-volumes-backup-failure.service
[Unit]
Description=Alerta de fallo en backup Docker
[Service]
Type=oneshot
ExecStart=/usr/bin/mail -s "ALERTA: fallo backup Docker en $(hostname)" admin@dominio.com <<< "El backup de volúmenes Docker ha fallado. Revisa journalctl -u docker-volumes-backup.service"
Ahora vincula el OnFailure en el servicio principal. Edita docker-volumes-backup.service:
sudo nano /etc/systemd/system/docker-volumes-backup.service
Y añade la línea OnFailure=docker-volumes-backup-failure.service en la sección [Unit]:
[Unit]
Description=Backup de volúmenes Docker con Restic
Wants=network-online.target
After=network-online.target
OnFailure=docker-volumes-backup-failure.service
Recarga y ya está:
sudo systemctl daemon-reload
Para probar que la alerta funciona, simula un fallo temporalmente cambiando la ruta del script a una inexistente y ejecuta el servicio.
7. Verificación y restauración de respaldo
Listar snapshots:
source /opt/backups/restic-env
sudo -E restic snapshots
Montar un snapshot como sistema de archivos (vía FUSE) para inspeccionar:
sudo -E restic mount /mnt/restic-mount
# Navegar por /mnt/restic-mount y desmontar después: sudo umount /mnt/restic-mount
Restaurar un volumen específico a un directorio temporal:
sudo -E restic restore latest --target /tmp/restore-test --path /var/lib/docker/volumes/mi_volumen
8. Errores frecuentes y soluciones
| Error | Causa | Solución |
|---|---|---|
Fatal: unable to open repo: Is there a repo at ...? |
Repositorio no inicializado o ruta incorrecta | Ejecuta restic init correctamente luego de cargar las variables de entorno. |
Fatal: wrong password or no key found |
Contraseña incorrecta o perdida | Asegúrate de que RESTIC_PASSWORD esté exportada correctamente y que el archivo restic-env tenga permisos 600. |
permission denied al acceder a /var/lib/docker/volumes |
El script se ejecuta sin privilegios | Añade User=root en el servicio systemd o ejecuta con sudo conservando variables (sudo -E). |
| El timer no se activa | Persistent está en false y el sistema estuvo apagado en la hora programada |
Cambia Persistent=true para que ejecute en el siguiente arranque si se perdió la ventana. |
Los snapshots no se borran tras forget |
Falta --prune |
Restic solo marca los snapshots para olvido; con --prune se eliminan los datos reales. |
restic no encuentra comando mount |
Falta FUSE | Instala fuse y asegúrate de que el módulo del kernel esté cargado (modprobe fuse). |
9. Casos prácticos de uso
9.1 Backup a Backblaze B2
Añade al archivo restic-env:
export RESTIC_REPOSITORY="b2:nombre-bucket:/docker-backups"
export B2_ACCOUNT_ID="tu-account-id"
export B2_ACCOUNT_KEY="tu-application-key"
export RESTIC_PASSWORD="password-muy-fuerte"
9.2 Deduplicación entre múltiples hosts
Usa el mismo repositorio S3 desde diferentes servidores añadiendo --host en el comando de backup para diferenciar orígenes:
restic backup --host servidor-web-1 --tag prod /var/lib/docker/volumes
Restic deduplica bloques incluso entre máquinas distintas.
9.3 Backup pre- y post-script para bases de datos
Para PostgreSQL, antes del backup principal, vuelca la base de datos a un archivo en un volumen o directorio temporal:
docker exec postgres pg_dump -U user db > /var/lib/docker/volumes/dumps/db.sql
Añade esa línea al inicio del script de backup, y al final opcionalmente la eliminas. Así la copia incluye el dump consistente.
10. Buenas prácticas
- Cifra el archivo de entorno con
gpgo usa un gestor de secretos, especialmente si contiene claves de nube. - Ubica el repositorio local en un disco externo y no en la misma unidad que respaldas. Si es remoto, usa siempre transporte TLS.
- Prueba la restauración periódicamente (al menos una vez al mes) en un entorno de staging.
- Configura
RETENTION_*según la importancia de los datos. No acumules snapshots infinitos; Restic puede consumir mucho espacio si no pruning. - Añade
--verbosey redirige logs a un archivo rotativo o a syslog para depuración. - Usa
RESTIC_PASSWORD_COMMANDen lugar deRESTIC_PASSWORDsi prefieres leer la contraseña desde un archivo secreto o comando (ej.pass). - Monitoriza el timer con
systemd(p.ej.,systemctl status docker-volumes-backup.timer) y añade un chequeo de antigüedad del último snapshot en tu monitorización (Prometheus + node_exporter textfile collector).
11. Cierre con idea clave
Los backups manuales no son backups. Con Restic y systemd consigues un sistema de copias de seguridad de volúmenes Docker que es automático, cifrado, con retención inteligente y alertas. Una vez configurado, olvídate del miedo a perder datos y dedica tu tiempo a lo que realmente importa: construir tus aplicaciones.