Copias de seguridad de volúmenes Docker que realmente restauran

¿Te fue útil?

La dolorosa verdad: la mayoría de las “copias de seguridad” de Docker son copias optimistas de ficheros que nadie ha restaurado bajo presión. La primera vez que lo intentas es cuando falla un disco, un ingeniero está cansado y tu VP descubre qué significa “RPO”.

Esta guía es para entornos de producción. No para demostraciones. Haremos copias de seguridad de volúmenes Docker de formas que sobrevivan la realidad, y demostraremos que las restauraciones funcionan con simulacros repetibles y comprobaciones medibles.

Qué es lo que realmente estás respaldando

“Respaldar el contenedor” es una frase que suena razonable y suele ser incorrecta.

Los contenedores son procesos efímeros más una imagen. La imagen (normalmente) puede reconstruirse. El verdadero radio de impacto está en los datos que viven fuera de la imagen:

  • Volúmenes con nombre (gestionados por Docker; por lo general bajo /var/lib/docker/volumes).
  • Bind mounts (rutas del host montadas en contenedores; a menudo “solo una carpeta”, hasta que deja de serlo).
  • Secrets/config (variables de entorno, archivos montados, secretos de swarm, ficheros Compose, unidades systemd).
  • Servicios externos (bases de datos gestionadas, almacenamiento de objetos) de los que depende tu contenedor pero que no contiene.

Respaldar volúmenes Docker trata principalmente de la integridad a nivel de sistema de ficheros y de la consistencia a nivel de aplicación. Integridad de ficheros significa que los bits se copian correctamente. Consistencia significa que la aplicación realmente puede leer esos bits después de restaurarlos.

Para bases de datos, “consistente” no es una sensación. Es un estado. O usas herramientas nativas de la base de datos, o tomas snapshots del almacenamiento con la base de datos adecuadamente quiescida.

Datos interesantes (y por qué importan)

  1. Los volúmenes Docker fueron diseñados para desacoplar los datos del ciclo de vida del contenedor. Por eso “eliminar el contenedor” no elimina el volumen—hasta que alguien añade -v sin pensar.
  2. AUFS/OverlayFS popularizaron capas copy-on-write para contenedores. Genial para imágenes; irrelevante para tus datos persistentes, que viven en volúmenes o bind mounts.
  3. Los primeros usuarios de contenedores solían respaldar todo el directorio /var/lib/docker. “Funcionaba” hasta que cambiaron los drivers de almacenamiento o el host de restauración era distinto. La portabilidad fue la víctima.
  4. Los proveedores de bases de datos han predicado “backups lógicos” durante décadas porque las copias físicas de ficheros durante actividad de escritura pueden estar corruptas de forma silenciosa sin errores obvios en el momento de la copia.
  5. Los snapshots de sistema de ficheros (ZFS, LVM, btrfs) existen desde años antes que los contenedores. Los contenedores hicieron que los backups basados en snapshots volvieran a ponerse de moda porque requieren capturas rápidas, frecuentes y con baja sobrecarga.
  6. Tar es más antiguo que la mayoría de tu flota de producción. Sigue vigente porque es simple, streamable e integra bien con compresión y cifrado.
  7. RPO/RTO llegaron al vocabulario de las juntas tras incidentes de alto perfil. Los contenedores no cambiaron eso; solo facilitaron confundir “reconstruible” con “recuperable”.
  8. Las sumas de verificación no son opcionales en sistemas serios de backup. La corrupción silenciosa existe en todas las capas: RAM, disco, controlador, red, almacenamiento de objetos. Verifica o te sorprenderás.

Principios: haz esto, no aquello

1) Trata el “backup” como un flujo de trabajo de restauración que aún no has ejecutado

Un fichero de backup no es evidencia. Una restauración exitosa en un entorno limpio sí lo es. Tu objetivo es reducir la incertidumbre, no generar artefactos.

2) Separa “respaldo de datos” de “reconstrucción del servicio”

Mantén dos inventarios:

  • Inventario de reconstrucción: imágenes, ficheros Compose, configuraciones del sistema, proceso de emisión de TLS, gestión de secretos.
  • Inventario de datos: volúmenes, dumps/WAL/binlogs de bases de datos, ficheros subidos, colas, índices de búsqueda (y si puedes reconstruirlos).

3) Prefiere backups nativos de la aplicación para bases de datos

Para PostgreSQL, usa pg_dump o backups físicos con pg_basebackup (y WAL). Para MySQL/MariaDB, usa mysqldump o métodos físicos apropiados para tu motor. Tomar imágenes de ficheros crudos de una base de datos mientras escribe es apostar.

4) Cuando hagas backups a nivel de sistema de ficheros, controla la actividad de escritura

O bien:

  • Detén el contenedor de la aplicación (o ponlo en modo mantenimiento/solo lectura), luego copia; o
  • Usa snapshots en el sistema de ficheros del host; o
  • Usa hooks de quiescencia de la base de datos (flush/lock) y haz el snapshot rápido.

5) Haz los backups direccionables por contenido (o al menos verificados por checksum)

Como mínimo: guarda un manifiesto con lista de ficheros + tamaños + hashes. “El fichero existe” no es verificación.

6) Las pruebas de restauración deben ser aisladas y automatizadas

No restaures en el mismo host en las mismas rutas y declares victoria. Usa un host de pruebas o una VM desechable. Ejecuta una comprobación de salud que demuestre que el servicio lee los datos correctamente.

Broma n.º 1: Una copia de seguridad que nunca has restaurado es como un paracaídas que nunca has empaquetado: la confianza no es un plan de pruebas.

Una cita, porque sigue siendo cierta

La esperanza no es una estrategia. — atribuida en círculos de operaciones; trátala como una idea parafraseada, no como una cita exacta garantizada.

Tareas prácticas: comandos, salidas, decisiones (12+)

Estas son intencionadamente mundanas. Las fallas en producción suelen ser mundanas también.

Tarea 1: Lista volúmenes y detecta los que importan

cr0x@server:~$ docker volume ls
DRIVER    VOLUME NAME
local     app_postgres_data
local     app_uploads
local     monitoring_prometheus

Qué significa: existen tres volúmenes con nombre. Tus imágenes no aparecen porque los volúmenes son objetos independientes.

Decisión: clasifica cada volumen: “debe restaurarse” (bases de datos, uploads), “agradable tener” (caches), “reconstruible” (Prometheus podría ser reconstruible dependiendo de las necesidades de retención).

Tarea 2: Inspecciona un volumen para saber dónde vive

cr0x@server:~$ docker volume inspect app_postgres_data
[
  {
    "CreatedAt": "2025-11-02T12:44:10Z",
    "Driver": "local",
    "Labels": {
      "com.docker.compose.project": "app"
    },
    "Mountpoint": "/var/lib/docker/volumes/app_postgres_data/_data",
    "Name": "app_postgres_data",
    "Options": null,
    "Scope": "local"
  }
]

Qué significa: estás usando el driver local; los datos son un directorio en el host. Esto es simple y peligroso: simple de respaldar, fácil de corromper si se copia en caliente.

Decisión: decide si harás backups nativos de la aplicación (preferido para BD) o snapshot/copia en frío.

Tarea 3: Encuentra qué contenedores usan un volumen (comprobación del radio de impacto)

cr0x@server:~$ docker ps --format '{{.Names}}\t{{.Mounts}}'
app-web        /var/lib/docker/volumes/app_uploads/_data
app-db         /var/lib/docker/volumes/app_postgres_data/_data
prometheus     /var/lib/docker/volumes/monitoring_prometheus/_data

Qué significa: cada volumen está montado por un contenedor. Eso es bueno: menos escritores.

Decisión: si varios escritores montan el mismo volumen, tu plan de consistencia debe ser más explícito (locks, quiesce o rediseño).

Tarea 4: Identifica bind mounts que se esconden a simple vista

cr0x@server:~$ docker inspect app-web --format '{{json .Mounts}}'
[{"Type":"bind","Source":"/srv/app/config","Destination":"/etc/app","Mode":"ro","RW":false,"Propagation":"rprivate"},{"Type":"volume","Name":"app_uploads","Source":"/var/lib/docker/volumes/app_uploads/_data","Destination":"/var/www/uploads","Driver":"local","Mode":"z","RW":true,"Propagation":""}]

Qué significa: tienes un bind mount en /srv/app/config. Si solo respaldas volúmenes Docker, te perderás la configuración—luego las restauraciones “funcionan” pero el servicio no arrancará.

Decisión: añade las rutas de bind mounts al alcance del backup, o migra la configuración a un sistema gestionado de configuración.

Tarea 5: Revisa el espacio libre antes de generar un archivo gigante

cr0x@server:~$ df -h /var/lib/docker
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  450G  380G   48G  89% /

Qué significa: solo 48G libres. Un tarball de un volumen grande podría llenar el disco y dejar Docker inoperativo.

Decisión: haz streaming de backups hacia fuera del host (pipe hacia almacenamiento), o haz snapshot y transfiere, o libera espacio primero.

Tarea 6: Obtén el tamaño del volumen rápidamente (aprox., pero útil)

cr0x@server:~$ sudo du -sh /var/lib/docker/volumes/app_postgres_data/_data
23G	/var/lib/docker/volumes/app_postgres_data/_data

Qué significa: ~23G en disco. La compresión puede ayudar (o no, dependiendo de los datos).

Decisión: planifica retención y tiempo de transferencia. 23G noche tras noche sobre un enlace estrecho se convierte en una excusa semanal.

Tarea 7: Backup en frío de un volumen con tar (seguro para datos no-BD o BD parada)

cr0x@server:~$ docker stop app-web
app-web
cr0x@server:~$ docker run --rm -v app_uploads:/data:ro -v /backup:/backup alpine:3.20 sh -c 'cd /data && tar -cpf /backup/app_uploads.tar .'
cr0x@server:~$ docker start app-web
app-web

Qué significa: el backup se ejecuta en un contenedor desechable que monta el volumen en modo solo lectura y escribe un tar en /backup (un directorio del host que debes provisionar).

Decisión: si detener la app es inaceptable, pasa a backups basados en snapshots o nativos de la aplicación.

Tarea 8: Añade compresión y un manifiesto con checksum

cr0x@server:~$ docker run --rm -v app_uploads:/data:ro -v /backup:/backup alpine:3.20 sh -c 'cd /data && tar -cpf - . | gzip -1 > /backup/app_uploads.tar.gz'
cr0x@server:~$ sha256sum /backup/app_uploads.tar.gz
9c1e6311d2c51d6f9a9b8b3f5d65ed3db3f87e96a57c4e1b2f5c34b1b1a4d9a0  /backup/app_uploads.tar.gz

Qué significa: ahora tienes una comprobación de integridad. Guarda el checksum junto al artefacto en tu repositorio de backups.

Decisión: si no puedes generar y verificar checksums, no tienes un backup operativo—solo un fichero.

Tarea 9: Backup lógico de PostgreSQL desde dentro del contenedor (preferido para portabilidad)

cr0x@server:~$ docker exec -t app-db sh -c 'pg_dump -U postgres -Fc appdb' > /backup/appdb.dump
cr0x@server:~$ ls -lh /backup/appdb.dump
-rw-r--r-- 1 cr0x cr0x 3.2G Dec  7 02:10 /backup/appdb.dump

Qué significa: un volcado en formato personalizado que soporta restauración en paralelo y es más resistente entre versiones menores (dentro de lo razonable).

Decisión: si el dump es mucho más pequeño de lo esperado, verifica que volcaste la base correcta y que no volcaste un esquema vacío por accidente.

Tarea 10: Prueba de restauración de PostgreSQL en un contenedor desechable (prueba, no teoría)

cr0x@server:~$ docker run --rm --name pg-restore-test -e POSTGRES_PASSWORD=test -d postgres:16
2f4a6f88d3c8e6d0b0f14a27e8c2e6d84e8c4b7f6ddc5a8c1d2b3a4f5e6d7c8b
cr0x@server:~$ sleep 3
cr0x@server:~$ cat /backup/appdb.dump | docker exec -i pg-restore-test sh -c 'createdb -U postgres appdb && pg_restore -U postgres -d appdb'
cr0x@server:~$ docker exec -t pg-restore-test psql -U postgres -d appdb -c 'select count(*) from users;'
 count 
-------
 10492
(1 row)
cr0x@server:~$ docker stop pg-restore-test
pg-restore-test

Qué significa: restauraste en una base limpia y ejecutaste una consulta de sanidad. Eso es evidencia real.

Decisión: si los conteos no concuerdan con lo esperado, deja de llamarlo “verificado”. Investiga antes de que la retención rote y desaparezca tu última copia buena.

Tarea 11: Restaura un tarball de volumen con nombre en un volumen nuevo

cr0x@server:~$ docker volume create app_uploads_restore_test
app_uploads_restore_test
cr0x@server:~$ docker run --rm -v app_uploads_restore_test:/data -v /backup:/backup alpine:3.20 sh -c 'cd /data && tar -xpf /backup/app_uploads.tar'
cr0x@server:~$ docker run --rm -v app_uploads_restore_test:/data alpine:3.20 sh -c 'ls -lah /data | head'
total 64K
drwxr-xr-x    5 root     root        4.0K Dec  7 02:24 .
drwxr-xr-x    1 root     root        4.0K Dec  7 02:24 ..
drwxr-xr-x   12 root     root        4.0K Dec  5 19:11 images
drwxr-xr-x    3 root     root        4.0K Dec  6 08:33 tmp

Qué significa: puedes desempaquetar el archivo y ver los directorios de primer nivel esperados.

Decisión: si los permisos/propietarios importan (y lo hacen), valídalos con un fichero representativo y asegúrate de que tu tar los preservó (-p ayuda cuando se ejecuta como root).

Tarea 12: Verifica la integridad del archivo antes de restaurar (detecta transferencias corruptas)

cr0x@server:~$ sha256sum -c /backup/app_uploads.tar.gz.sha256
/backup/app_uploads.tar.gz: OK

Qué significa: el artefacto coincide con el hash esperado.

Decisión: si ves FAILED, detente. No restaures basura en producción. Recupera otro backup.

Tarea 13: Detecta ficheros abiertos para juzgar si una “copia en vivo” es riesgosa

cr0x@server:~$ sudo lsof +D /var/lib/docker/volumes/app_postgres_data/_data | head
postgres  22114  999  15u   REG  259,2  16777216  393222 /var/lib/docker/volumes/app_postgres_data/_data/base/16384/2619
postgres  22114  999  16u   REG  259,2  16777216  393223 /var/lib/docker/volumes/app_postgres_data/_data/base/16384/2620

Qué significa: PostgreSQL está escribiendo activamente. Copiar esos ficheros ahora mismo no es un backup; es un generador de corrupción.

Decisión: usa pg_dump/pg_basebackup o detén/quiesce + snapshot.

Tarea 14: Mide el rendimiento de restauración (conoce tu RTO, no lo adivines)

cr0x@server:~$ time docker run --rm -v app_uploads_restore_test:/data -v /backup:/backup alpine:3.20 sh -c 'cd /data && rm -rf ./* && tar -xpf /backup/app_uploads.tar'
real	0m18.412s
user	0m0.812s
sys	0m3.951s

Qué significa: tu restauración tomó ~18 segundos para este conjunto de datos en este host. Ese es el número que usas para planificar RTO (más el tiempo de calentamiento del servicio).

Decisión: si la restauración es lenta, no optimices al azar—ve a la guía de diagnóstico rápido.

Métodos de respaldo que aguantan

Método A: Backups lógicos para bases de datos (recomendado)

Si tienes un volumen de base de datos y lo estás taring “porque es fácil”, para. Usa el mecanismo de backup de la base de datos. Obtienes:

  • Portabilidad entre hosts y drivers de almacenamiento
  • Consistencia garantizada por el motor
  • Mejor resolución de problemas: la restauración te dirá qué está mal

Para PostgreSQL, una base sólida es un pg_dump -Fc diario más archivado WAL si necesitas recuperación punto en el tiempo. Para MySQL, una base es mysqldump o backups físicos específicos del motor con binlogs.

Compensación: los backups lógicos pueden ser más lentos y más grandes para algunas cargas, y las restauraciones pueden ser más lentas que las de nivel de fichero. Es una decisión de negocio—pero tómatela explícitamente.

Método B: Backup “en frío” del sistema de ficheros de un volumen (detén el escritor)

Para uploads, configuraciones, artefactos y datos no transaccionales: detener el contenedor (o asegurar que no hay escrituras) y copiar el volumen es directo.

  • Pros: simple, rápido, fácil de entender
  • Contras: requiere downtime o congelación de escrituras; debe preservar propietario/ACLs/xattrs si aplica

Si tu app usa capacidades de Linux, etiquetas SELinux o ACLs, tu comando tar debe preservarlas. El tar de Alpine sirve para permisos básicos; si necesitas xattrs/ACLs, usa un contenedor de backup con GNU tar y las flags que correspondan a tu entorno.

Método C: Backups basados en snapshots en el sistema de ficheros del host (rápido, baja indisponibilidad)

Si el directorio de datos de Docker vive en ZFS, LVM-thin o btrfs, puedes snapshotear el dataset/volumen subyacente rápidamente y luego copiar desde el snapshot mientras la producción continúa.

Importante: snapshotear un sistema de ficheros no convierte mágicamente a la aplicación en consistente. Para bases de datos, combina snapshots con quiescencia adecuada o modo de backup del motor; de lo contrario puedes snapshotear un sistema perfectamente consistente que contiene un estado de base de datos inconsistente.

Método D: Drivers de volúmenes remotos / almacenamiento en red

Algunos equipos usan NFS, iSCSI, Ceph o almacenamiento en bloque en la nube detrás de un driver de volumen Docker. Esto puede estar bien, pero desplaza el problema de backup:

  • Ahora respaldas el sistema de almacenamiento, no Docker.
  • Las restauraciones pueden requerir el mismo driver y configuración.
  • La latencia y el comportamiento en escrituras pequeñas pueden perjudicar a las bases de datos.

Cuando usas un driver remoto, documenta el nombre del driver, las opciones y el ciclo de vida. Si tu plan de restauración empieza con “simplemente volveremos a adjuntar el volumen”, necesitas un plan B para cuando ese sistema sea el que esté en llamas.

Método E: “Backup” basado en imagen (la trampa)

La gente propondrá “docker commit el contenedor”. Eso produce una capa de imagen con el sistema de ficheros del contenedor en ese momento. No captura volúmenes con nombre. Rara vez captura bind mounts. También es una buena forma de conservar secretos en una imagen para siempre. No lo uses para backups de datos.

Broma n.º 2: “Respaldamos el contenedor” es la manera de obtener una imagen preciosa de un servicio que ha olvidado todo lo que sabía.

Cómo demostrar que las restauraciones funcionan (no solo “se extrajo”)

Define “funciona” como un contrato comprobable

Una restauración “funciona” cuando:

  • Los datos se restauran en un entorno limpio sin ajustes manuales.
  • El servicio arranca con los datos restaurados.
  • Un pequeño conjunto de comprobaciones de comportamiento pasan: consultas devuelven filas esperadas, uploads son legibles, migraciones se comportan, y los logs no muestran corrupción.
  • El equipo puede hacerlo bajo presión temporal con un runbook.

Construye un simulacro de restauración que se ejecute con calendario

Elige una cadencia sostenible: semanal para datos críticos, mensual para menos críticos, después de cada cambio mayor de esquema. El simulacro debe:

  1. Descargar el artefacto de backup más reciente.
  2. Verificar checksums.
  3. Restaurar en infraestructura desechable (VM, host efímero o red Docker aislada).
  4. Ejecutar una suite corta de validación.
  5. Publicar resultados en un lugar visible (ticket, canal de Slack, tablero), incluyendo razones de fallo.

Suite de validación: ejemplos que realmente detectan problemas

  • Base de datos: ejecuta consultas SELECT para conteos de filas en tablas clave; verifica que las migraciones pueden ejecutarse en modo dry-run si está disponible; confirma que existen índices; confirma que hay timestamps recientes.
  • Uploads: elige 10 ficheros conocidos y verifica checksum o al menos tamaño; asegura que los permisos permiten que el usuario de la app los lea.
  • Arranque de la app: revisa logs por patrones malos conocidos (permission denied, claves de configuración faltantes, mismatch de esquema).

Demuestra RPO y RTO, no solo corrección

La corrección responde “¿podemos restaurar?” RPO/RTO responden “¿podemos restaurar a tiempo, con pérdida de datos aceptable?” Mídelo:

  • RPO: tiempo entre el último backup exitoso y el momento del incidente (usa timestamps de backup, no intuiciones).
  • RTO: tiempo desde “iniciamos la restauración” hasta “el servicio cumple el SLO otra vez”. Incluye tiempo de obtención de artefactos, descompresión, verificación y calentamiento de caches.

Mantén un “kit de restauración” con todo lo que no es el dato

La mayoría de las restauraciones fallidas no se deben a “datos malos”. Son por faltas de pegamento:

  • Fichero Compose con versión fijada y almacenado
  • Imágenes de contenedores versionadas (o pipeline de build reproducible)
  • Ruta de gestión de secretos documentada
  • Red/puertos, configuración del reverse proxy, proceso de renovación TLS
  • Notas de compatibilidad de versión de la base de datos

Guía de diagnóstico rápido

Los backups y restauraciones fallan de maneras predecibles. No empieces con una reescritura a ciegas de tus scripts. Empieza por acotar el cuello de botella.

Primero: ¿la falla es de consistencia o de mecánica?

  • Mecánica: tar falla, mismatch de checksum, permiso denegado, sin espacio, transferencia lenta.
  • Consistencia: la restauración termina pero la app da errores, la BD reporta corrupción, falta data reciente.

Segundo: Identifica la etapa más lenta

  1. Obtención del artefacto (red/almacenamiento de objetos)
  2. Descompresión (ligado a CPU)
  3. Extracción/escritura (ligado a disco, inodos)
  4. Recuperación de la aplicación (reproducción de BD, migraciones)

Tercero: Comprobaciones rápidas que dan respuestas pronto

Revisa saturación de disco (la restauración escribe mucho)

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server) 	12/07/2025 	_x86_64_	(8 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.31    0.00    8.14   34.22    0.00   45.33

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1         12.0   1456.0     0.0    0.0    1.20   121.3     980.0  84224.0   120.0   10.9   18.50    85.9    18.2   98.0

Qué significa: %util cerca de 100% y w_await elevado implican que el disco es el limitante.

Decisión: reduce el paralelismo de la restauración, restaura en almacenamiento más rápido, o evita formatos de compresión que amplifiquen la escritura.

Revisa si la descompresión consume CPU

cr0x@server:~$ top -b -n 1 | head -n 15
top - 02:31:20 up 21 days,  4:12,  1 user,  load average: 7.92, 8.10, 6.44
Tasks: 212 total,   2 running, 210 sleeping,   0 stopped,   0 zombie
%Cpu(s): 92.1 us,  0.0 sy,  0.0 ni,  5.8 id,  0.0 wa,  0.0 hi,  2.1 si,  0.0 st
MiB Mem :  32158.5 total,   812.4 free,  14220.1 used,  17126.0 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.  17938.4 avail Mem

Qué significa: la CPU está al máximo en espacio de usuario; la descompresión o el cálculo de checksums puede ser el punto caliente.

Decisión: usa compresión más rápida (gzip -1 vs fuerte), herramientas de descompresión paralelas, o almacena sin comprimir en redes internas rápidas donde el disco sea el limitante de todos modos.

Revisa agotamiento de inodos (clásico para muchos ficheros pequeños)

cr0x@server:~$ df -ih /var/lib/docker
Filesystem     Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2   28M   27M  1.0M   97% /

Qué significa: puedes tener espacio libre pero sin inodos; las restauraciones fallan con “No space left on device” mientras df -h parece bien.

Decisión: mueve volúmenes a un sistema de ficheros con más inodos o ajusta parámetros de creación del FS; reduce churn de ficheros muy pequeños; considera empaquetar en almacenamiento de objetos cuando sea apropiado.

Revisa desajuste de permisos/propietarios (común tras restores con tar)

cr0x@server:~$ docker exec -t app-web sh -c 'id && ls -ld /var/www/uploads'
uid=1000(app) gid=1000(app) groups=1000(app)
drwxr-xr-x    5 root     root        4096 Dec  7 02:24 /var/www/uploads

Qué significa: la app corre como UID 1000, pero el directorio está en poder de root. Las lecturas pueden funcionar; las escrituras fallarán.

Decisión: arregla la propiedad en el procedimiento de restauración (p. ej., chown -R 1000:1000), o ejecuta backup/restore preservando la propiedad y asegurando el mapeo correcto de usuarios.

Errores comunes: síntoma → causa raíz → solución

1) “La restauración terminó, pero la app falta datos recientes”

Síntoma: el servicio arranca, pero faltan datos del último día/semana.

Causa raíz: respaldaste el volumen equivocado, la base de datos equivocada o el entorno equivocado; o el trabajo de backup falló silenciosamente y seguiste rotando artefactos vacíos.

Solución: aplica un mapeo de inventario obligatorio (nombre volumen/base → nombre del artefacto). Haz que el trabajo falle si el tamaño del dump está por debajo de un umbral. Ejecuta simulacros programados de restauración con consultas de sanidad.

2) “El backup en tar existe, pero la restauración da permiso denegado”

Síntoma: los logs de la app muestran errores de permisos al escribir en directorios restaurados.

Causa raíz: restaurar como root sin coincidir expectativas de UID/GID; pérdida de ACLs/xattrs; o contenedores que corren con usuarios no root.

Solución: captura y restaura propiedad explícitamente; usa un paso de restauración que aplique el UID/GID correcto; verifica con una prueba de escritura como el usuario de la app.

3) “La base de datos no arranca después de copiar el volumen”

Síntoma: PostgreSQL o MySQL se niega a arrancar, se queja de WAL/binlog o páginas corruptas.

Causa raíz: copia a nivel de ficheros tomada mientras la BD escribía; snapshot incompleto; estado inconsistente.

Solución: usa backup lógico o backup físico adecuado. Si debes snapshotear, quiesce correctamente y haz el snapshot de forma atómica.

4) “El job de backup tarda una eternidad y provoca picos de latencia”

Síntoma: la latencia de I/O en producción sube durante backups; la app se ralentiza.

Causa raíz: las lecturas del backup compiten con lecturas de producción; la compresión consume CPU; el almacenamiento está saturado; demasiados ficheros pequeños.

Solución: limita la tasa de lectura del backup; programa en horas valle; usa snapshots; cambia el nivel de compresión; rediseña el layout de datos.

5) “Restauramos el volumen, pero la app sigue apuntando a datos antiguos”

Síntoma: la restauración parece exitosa pero el servicio sirve contenido obsoleto.

Causa raíz: restauraste en un volumen nuevo pero el fichero Compose sigue referenciando el antiguo; o las rutas de bind mount difieren.

Solución: intercambia explícitamente las referencias de volumen; usa nombres de proyecto de prueba únicos; confirma los mounts del contenedor con docker inspect.

6) “La verificación de checksum falla ocasionalmente”

Síntoma: fallos aleatorios de hash entre días.

Causa raíz: subidas parciales, escrituras no atómicas al almacén de backups, o transferencias de red inestables.

Solución: escribe a un nombre temporal y renombra de forma atómica; guarda y verifica manifiestos; asegúrate de que tu herramienta de subida use verificación multipart; reintenta en caso de mismatch y alerta.

Tres mini-historias corporativas (anonimizadas)

Mini-historia 1: Un incidente causado por una suposición equivocada

El equipo ejecutaba una app orientada al cliente con un contenedor PostgreSQL y un volumen con nombre. Su script de backup hacía un tar del volumen cada noche. Parecía limpio. Incluso tenía timestamps y retención. Todos dormían tranquilos.

Entonces un on-call recibió una alerta por una falla de host. El plan de recuperación era simple: aprovisionar una nueva VM, restaurar el tar en un volumen fresco, arrancar el contenedor BD. Arrancó… y se cayó inmediatamente. Los logs mencionaban problemas de WAL y un estado de base de datos que “parece copiado mientras escribía”. Eso fue porque lo era.

La suposición equivocada fue sutil: “una copia del sistema de ficheros es un backup”. El script corría a las 2 a.m. cuando la carga era baja, pero no cero. PostgreSQL todavía escribía. La copia produjo un archivo internamente inconsistente que tar nunca podría detectar.

Eventualmente recuperaron desde un backup más antiguo que por suerte era consistente (la noche más tranquila del mes). Después, pasaron a pg_dump más un simulacro semanal de restauración en un contenedor desechable. La parte aburrida—probar restauraciones—se volvió la parte que importaba a los ejecutivos.

Mini-historia 2: Una optimización que salió mal

Otra organización tenía una montaña de uploads de usuarios. Los backups eran demasiado lentos, así que alguien los “optimizó” con compresión máxima para ahorrar ancho de banda y almacenamiento. Los artefactos se redujeron impresionante. A todos les gustó la gráfica de costos.

Las restauraciones nunca se probaron a escala. La primera restauración real ocurrió durante un incidente de seguridad donde tuvieron que reconstruir hosts rápido. Tiraron del backup y empezaron a descomprimir. La CPU se fue al techo. Las escrituras en disco se encolaron. La tubería de restauración tardó horas más de lo que el RTO que habían prometido en una diapositiva.

El problema técnico no fue que la compresión sea mala. Fue que optimizaron una sola métrica (bytes almacenados) sin medir el tiempo de restauración. Además descomprimieron en nodos de recuperación con limitación de CPU, convirtiendo la recuperación en una tarea ligada a cómputo.

La solución fue poco glamorosa: cambiar a compresión rápida (o ninguna) para backups de la capa caliente, y mantener una copia más comprimida a largo plazo para archivo. También empezaron a medir el throughput de restauración como métrica de primera clase. Los costos subieron un poco. La fatiga por paginación bajó mucho.

Mini-historia 3: Una práctica aburrida pero correcta que salvó el día

Un SaaS financiero ejecutaba múltiples servicios con Docker Compose. Sus runbooks incluían un ticket semanal de “ensayo de restauración”. El on-call restauraba el dump BD más reciente en un contenedor desechable, levantaba el stack de la app en una red aislada y ejecutaba unas llamadas API.

Sin heroísmos. Solo repetición. Registraban tiempos: tiempo de descarga, tiempo de restauración, tiempo hasta la primera petición exitosa. Si los números se desviaban, investigaban mientras nadie estaba en pánico.

Un fin de semana, un incidente de almacenamiento corrompió el sistema de ficheros de un host. Reconstruyeron un nodo, restauraron volúmenes y dumps BD, y volvieron a estar en línea sin drama. Lo que parecía suerte era sólo memoria muscular. El equipo ya sabía qué artefactos eran válidos, cuánto tardaba la restauración y qué comandos fallarían si algo estaba mal.

La verdadera ganancia: no tuvieron que improvisar. Ejecutaron un flujo de trabajo practicado y volvieron a molestar por las alertas de monitorización, que es el estado emocional ideal para on-call.

Listas de verificación / plan paso a paso

Checklist A: Construye tu inventario de backups (una tarde)

  1. Lista volúmenes: docker volume ls.
  2. Lista bind mounts: docker inspect en cada contenedor y extrae Mounts.
  3. Clasifica datos: base de datos / uploads / cache / reconstruible.
  4. Define RPO y RTO por clase (aunque sea aproximado es mejor que silencio).
  5. Apunta requisitos de propiedad/permisos (UID/GID, ACLs, SELinux).

Checklist B: Implementa backups (scripts repetibles)

  1. Para bases de datos: implementa backup lógico (o físico correcto) y almacena artefactos fuera del host.
  2. Para volúmenes de ficheros: elige copia en frío o basada en snapshot; evita copia en vivo para escritores.
  3. Crea un manifiesto por artefacto: timestamp, origen, tamaño, checksum, versión de la herramienta.
  4. Haz los backups atómicos: escribe en temporal y luego renombra; nunca dejes artefactos parciales con nombre “final”.
  5. Alerta en fallos y en salidas sospechosamente pequeñas.

Checklist C: Demuestra restauraciones (simulacro semanal o mensual)

  1. Descarga el artefacto más reciente.
  2. Verifica checksum.
  3. Restaura en un entorno limpio (volumen nuevo/contenedor nuevo/proyecto Compose nuevo).
  4. Ejecuta checks de validación (consultas, comprobación de ficheros, endpoints de salud de la app).
  5. Registra tiempos y resultados; crea un ticket por cualquier desviación.

Checklist D: Runbook de restauración en incidente (cuando ya está mal)

  1. Detén la hemorragia: evita que los escritores sigan (modo mantenimiento, detener contenedores).
  2. Identifica el “último backup bueno” a partir de los logs del simulacro de restauración, no de la esperanza.
  3. Restaura en volúmenes nuevos; no sobrescribas evidencia salvo que sea imprescindible.
  4. Levanta servicios en orden de dependencias (BD primero, luego app, luego workers).
  5. Valida externamente (chequeos sintéticos) e internamente (logs, check de integridad BD).
  6. Después de la recuperación: preserva el disco/volumen roto para forense si es necesario.

Preguntas frecuentes

1) ¿Debo respaldar /var/lib/docker?

Generalmente no. No es portable entre drivers de almacenamiento, versiones de Docker y layouts de host. Respaldá los datos (volúmenes y bind mounts) y las definiciones (ficheros Compose, configs), por separado.

2) ¿Un tar de un volumen es siempre seguro?

Es seguro si los datos no están siendo modificados o si la aplicación tolera copias crash-consistent. Para bases de datos, asume que no es seguro a menos que quiescees apropiadamente o uses backups nativos de la BD.

3) ¿Cuál es la diferencia entre un volumen con nombre y un bind mount para backups?

Los volúmenes con nombre los gestiona Docker y viven bajo el directorio de datos de Docker. Los bind mounts son rutas arbitrarias del host. Desde la perspectiva de backup, los bind mounts son más fáciles de integrar con herramientas de backup del host—hasta que alguien cambia la ruta y olvida actualizar el alcance del backup.

4) ¿Cómo respaldo volúmenes con Docker Compose?

Compose es solo orquestación. La mecánica de backup es la misma: usa docker exec para backups lógicos de BD, y docker run --rm con mounts de volumen para backups de sistema de ficheros. Lo importante es la consistencia de nombres para que tus scripts encuentren los volúmenes correctos en cada entorno.

5) ¿Puedo usar docker commit como backup?

No para datos persistentes. No incluirá volúmenes con nombre y puede capturar secretos en una capa de imagen. Es útil ocasionalmente para depurar el estado del sistema de ficheros de un contenedor, no para recuperación ante desastres.

6) ¿Con qué frecuencia debo probar restauraciones?

Tantas veces como tu negocio pueda permitirse fallar. Semanal para bases de datos críticas es común; mensual para conjuntos de datos menos críticos. También prueba después de cambios mayores de esquema, migraciones de almacenamiento o reconstrucciones de host Docker.

7) ¿Necesito cifrado para backups de volúmenes?

Si los backups contienen datos de clientes, credenciales o código propietario, sí. Cifra en reposo y en tránsito, y gestiona claves separadas del almacenamiento de backups. “Está en un bucket privado” no es un control, es una esperanza.

8) ¿Cómo manejo diferencias de UID/GID entre hosts?

Prefiere IDs numéricos estables para usuarios de servicio entre hosts. Si eso no es posible, incluye un paso post-restore de corrección de propiedad. Verifica realizando una escritura como el usuario en tiempo de ejecución del contenedor, no como root.

9) ¿Y backups incrementales para volúmenes enormes?

Se puede, pero aplica un coste de complejidad. Para volúmenes de ficheros, send/receive basados en snapshot (ZFS/btrfs) o incrementales con rsync pueden funcionar. Para bases de datos, usa WAL/binlogs o herramientas del proveedor. Sea lo que sea, tu simulacro de restauración debe incluir reconstruir desde incrementales—si no, tu “estrategia incremental” es teórica.

10) ¿Cuál es la mejora más fiable que puedo hacer?

Automatizar la verificación de restauraciones en un entorno aislado. Convierte “creemos” en “sabemos”, y detecta fallos silenciosos como dumps vacíos, objetivos erróneos y desajustes de permisos.

Conclusión: próximos pasos que puedes hacer hoy

  1. Haz inventario de tus datos: volúmenes, bind mounts y “cosas fuera de Docker” (secrets, configs).
  2. Elige primitivas de backup correctas: nativo de BD para bases de datos; copia en frío/snapshot para datos de ficheros.
  3. Añade integridad: checksums y manifiestos, almacenados con los artefactos.
  4. Programa un simulacro de restauración: restaura en un contenedor/stack desechable y ejecuta comprobaciones reales.
  5. Mide el RTO: cronometra la restauración de extremo a extremo, y decide si se ajusta a lo que puedes tolerar.

Si solo haces una cosa: restaura un backup en un entorno limpio esta semana y hazlo hábito. El puente de incidentes del futuro será más silencioso, más corto y mucho menos teatral.

← Anterior
Fail2ban para correo: reglas que realmente detectan ataques
Siguiente →
Docker: reglas de enrutamiento de Traefik que fallan silenciosamente — corrige las etiquetas correctamente

Deja un comentario