Backup del Homelab¶
Backup automático consistente cada 2 días a las 03:00. El script detiene temporalmente los contenedores que estaban activos, copia configuración, bind mounts y volúmenes Docker con nombre, crea un archivo Zstandard y lo sube cifrado a Backblaze B2.
Rutas y estado¶
| Qué | Dónde |
|---|---|
| Script | /opt/scripts/backup.sh (root, modo 700) |
| Staging y logs locales | /opt/backups/ (root:root, modo 700) |
| Log | /opt/backups/backup.log |
| Destino remoto | b2crypt:/ mediante rclone crypt |
| Cron root | 0 3 */2 * * /opt/scripts/backup.sh |
Los archivos se llaman homelab_AAAA-MM-DD_HH-MM.tar.zst y tienen un sidecar .sha256. Se conservan las últimas 7 copias cifradas en B2. El archivo local es solo staging: se elimina después de subir archivo y checksum y comprobar que el tamaño remoto coincide. Si la subida o verificación falla, queda local para reintentar o recuperar.
Cobertura¶
/opt/docker, configuraciones de WireGuard, crontabs y scripts del host.- Bind mounts descubiertos desde los contenedores, salvo sockets,
/etc/localtime,/etc/timezone, cachés y rutas excluidas por política. - Todos los Docker named volumes que estén conectados a un contenedor; se guarda también su
inspect.json. - Secretos necesarios para recuperación (
.env, WireGuard y ntfy). Por eso el staging local es exclusivamente de root y el remoto está cifrado.
Los datos bajo /mnt/hdd_1tb y /mnt/hdd_2tb no se incluyen cuando los discos no están montados. El script lo registra explícitamente. No asumir que un directorio vacío en /mnt representa al disco.
Protecciones operativas¶
flockimpide ejecuciones concurrentes.- Preflight exige al menos 25 GiB libres y verifica inodos.
- La salida se escribe como
.partialy se renombra atómicamente al finalizar. - Los errores de bind mounts se acumulan y producen estado no-cero.
- Cada contenedor se reinicia individualmente; un fallo de arranque se informa como estado degradado sin descartar una copia ya consistente.
- Se conserva y verifica el estado previo: solo vuelven a arrancar los contenedores que estaban activos.
Verificación y restore¶
# Ver el proceso y el log
sudo tail -f /opt/backups/backup.log
# Verificar integridad del último archivo
cd /opt/backups
sudo sha256sum -c homelab_AAAA-MM-DD_HH-MM.tar.zst.sha256
sudo zstd -t homelab_AAAA-MM-DD_HH-MM.tar.zst
# Listar sin extraer
sudo tar --use-compress-program=unzstd -tf homelab_AAAA-MM-DD_HH-MM.tar.zst | less
# Consultar las copias cifradas remotas
sudo rclone lsl b2crypt:/
Para restaurar, extraer primero en un directorio aislado. Restaurar configuración y volúmenes con Docker detenido; no sobrescribir /var/lib/docker completo. Para bases importantes, validar además que cada servicio abra sus datos luego del restore.
Pendientes¶
- Hacer y registrar un restore drill trimestral por servicio.
- Configurar retención GFS y Object Lock/immutability en B2 con credenciales de carga separadas de las de borrado.
- Incorporar dumps nativos de PostgreSQL/MongoDB/SurrealDB además de las copias en frío.
- Rehabilitar
backup_full.shsolo cuando/mnt/backup_512exista, esté montado por UUID y el job quede programado.
Incidentes conocidos¶
- 15-08-2026: el backup abortó al intentar reiniciar un contenedor obsoleto de
odyssey-dev. El último recuperable anterior era del 13-08. - 17-08-2026: se endureció el script y se validó una copia local/remota completa de 7,8 GiB, con 16 named volumes y SHA-256 correcto.
- 17-08-2026: se migró a retención B2-only. Se eliminaron seis duplicados locales ya verificados contra B2 y un
.tar.gzheredado;/opt/backupsconserva logs y staging solo ante fallo. La raíz pasó de 76% a 46%.