Backup de Volúmenes Docker en VPS: Guía Práctica de Persistencia

Aprende a hacer backup de volúmenes Docker en VPS sin detener contenedores. Estrategias para bases de datos, configs y datos persistentes en producción.

Los volúmenes Docker son el punto donde la abstracción de contenedor se encuentra con la realidad del disco. El contenedor es descartable — lo reconstruyes a partir de la imagen en minutos. El volumen es donde vive el estado real: base de datos, uploads de usuarios, configuraciones persistentes. Perder un volumen es perder datos de producción.

A pesar de esto, el backup de volúmenes es la parte más descuidada del stack Docker. La mayoría de las VPS ejecutando aplicaciones containerizadas no tienen rutina de backup configurada, solo la falsa seguridad de que “está todo en un contenedor”. Cuando el disco falla o alguien ejecuta docker volume prune distraído, la pérdida es definitiva.

Este tutorial muestra estrategias prácticas para hacer backup de volúmenes Docker en VPS Linux, cubriendo datos estáticos, bases de datos, y automatización con cron. Tiempo estimado de ejecución: 25-40 minutos para configurar el primer backup y validar la restauración.

Prerrequisitos

Lo que necesitas

VPS ejecutando Ubuntu 22.04 LTS o Debian 12 con Docker Engine 24+ instalado, al menos un volumen Docker en uso por algún contenedor, y acceso sudo. Espacio libre equivalente a 1.5x el tamaño del volumen original (para compresión temporal) e, idealmente, destino remoto para los backups (S3-compatible, otro servidor, o storage externo).

Confirma la versión de Docker y lista tus volúmenes antes de empezar:

docker --version
docker volume ls
Docker Engine 24.x o superior
Sistema Ubuntu 22.04+ / Debian 12+
Espacio libre 1.5x tamaño del volumen
Destino remoto S3, rsync o block storage

Identificando volúmenes y contenedores afectados

Antes de hacer backup, mapea qué contenedores usan cada volumen. Backup ciego sin entender las dependencias causa inconsistencia — puedes capturar el archivo de base de datos en medio de una transacción si el contenedor está escribiendo.

01

Lista todos los volúmenes nombrados y los contenedores que los utilizan:

docker volume ls --format '{{.Name}}'

Para cada volumen, identifica los contenedores consumidores:

docker ps -a --filter volume=nombre_del_volumen --format '{{.Names}}: {{.Image}}'

Los volúmenes anónimos (con hash largo) generalmente son descartables — vienen de imágenes que declaran VOLUME en el Dockerfile sin nombre explícito. Enfócate en los volúmenes nombrados.

02

Inspecciona el punto de montaje físico de cada volumen:

docker volume inspect nombre_del_volumen

El campo Mountpoint muestra dónde Docker almacena los datos — típicamente /var/lib/docker/volumes/<nombre>/_data. Esa ruta es la que necesitas para backup directo vía tar.

03

Verifica el tamaño real de cada volumen:

sudo du -sh /var/lib/docker/volumes/nombre_del_volumen/_data

Usa ese valor para dimensionar el espacio de destino y estimar el tiempo de backup. Volúmenes por encima de 20 GB merecen estrategia incremental.

Backup de datos estáticos con tar

Los datos estáticos — uploads de usuario, configs, assets — pueden copiarse con el contenedor en ejecución. El riesgo de inconsistencia es bajo porque las escrituras atómicas (mv, rename) preservan el estado anterior hasta que la operación se complete.

La estrategia estándar usa un contenedor temporal Alpine con el volumen montado en modo solo-lectura, creando el tarball hacia un directorio del host.

04

Crea un directorio para los backups en el host:

sudo mkdir -p /var/backups/docker-volumes
sudo chmod 700 /var/backups/docker-volumes

El permiso 700 garantiza que solo root lea los archivos — los backups frecuentemente contienen datos sensibles.

05

Ejecuta el backup de un volumen con un contenedor Alpine descartable:

docker run --rm \
  -v nombre_del_volumen:/source:ro \
  -v /var/backups/docker-volumes:/backup \
  alpine \
  tar czf /backup/volume-$(date +%Y%m%d-%H%M%S).tar.gz -C /source .

El flag :ro monta el volumen en solo-lectura — impide que el contenedor temporal modifique datos accidentalmente. El -C /source . evita incluir la ruta /source en la estructura del tarball, facilitando la restauración.

Los tarballs grandes consumen CPU

La compresión gzip es single-threaded — para volúmenes por encima de 10 GB, considera pigz (gzip paralelo) o zstd que escalan con los cores disponibles. En VPS de 2 vCPUs, gzip puro es aceptable; en 8+ vCPUs, pigz reduce el tiempo a la mitad.

Backup consistente de bases de datos

Las bases de datos son el caso más delicado. Copiar los archivos binarios (datadir de MySQL, base/ de PostgreSQL) con el motor en ejecución resulta en backup corrupto — escrituras en curso, WAL no-flushed, índices inconsistentes. La solución es usar el dump nativo del motor.

06

Para MySQL/MariaDB, ejecuta mysqldump directo desde el contenedor:

docker exec nombre_contenedor_mysql \
  sh -c 'mysqldump --single-transaction --quick \
    -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases' \
  | gzip > /var/backups/docker-volumes/mysql-$(date +%Y%m%d-%H%M%S).sql.gz

El flag --single-transaction crea un snapshot consistente en InnoDB sin bloquear escrituras — funciona porque el dump corre dentro de una transacción aislada. Para tablas MyISAM, agrega --lock-tables (pero considera migrar a InnoDB).

07

Para PostgreSQL, usa pg_dumpall o pg_dump por base de datos:

docker exec nombre_contenedor_pg \
  pg_dumpall -U postgres \
  | gzip > /var/backups/docker-volumes/postgres-$(date +%Y%m%d-%H%M%S).sql.gz

pg_dumpall incluye roles y configuraciones de cluster — necesario para restauración completa. Para databases individuales grandes, pg_dump -Fc (custom format) permite restauración paralela con pg_restore -j.

08

Para MongoDB, usa mongodump hacia un directorio temporal en el contenedor y tar después:

docker exec nombre_contenedor_mongo \
  mongodump --archive --gzip \
  > /var/backups/docker-volumes/mongo-$(date +%Y%m%d-%H%M%S).archive.gz

El formato archive concatena todos los databases en un único stream — más fácil de transferir que el directorio clásico de mongodump.

Contraseña en variable de entorno

Los ejemplos anteriores asumen que la contraseña está en $MYSQL_ROOT_PASSWORD dentro del contenedor. Nunca pases contraseña en el argumento -p$contraseña directo en el comando — queda visible en ps aux y en los logs del shell. Usa siempre variable de entorno o archivo .my.cnf montado.

Automatización con cron

El backup manual no escala — lo olvidas, o lo haces solo cuando te acuerdas (generalmente cuando necesitas restaurar). Cron resuelve esto. La receta a continuación crea un script encapsulando la rutina y agenda ejecución diaria.

09

Crea un script en /usr/local/bin/docker-backup.sh:

#!/bin/bash
set -euo pipefail

BACKUP_DIR=/var/backups/docker-volumes
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
RETENTION_DAYS=14

# Backup MySQL
docker exec mysql_container \
  sh -c 'mysqldump --single-transaction --quick \
    -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases' \
  | gzip > "$BACKUP_DIR/mysql-$TIMESTAMP.sql.gz"

# Backup volumen estatico
docker run --rm \
  -v uploads_volume:/source:ro \
  -v "$BACKUP_DIR":/backup \
  alpine \
  tar czf "/backup/uploads-$TIMESTAMP.tar.gz" -C /source .

# Limpia backups antiguos
find "$BACKUP_DIR" -name "*.gz" -mtime +$RETENTION_DAYS -delete

El set -euo pipefail aborta ante cualquier error — un backup parcial es peor que la ausencia de backup porque da una falsa sensación de seguridad.

10

Haz el script ejecutable y agéndalo en el cron de root:

sudo chmod 700 /usr/local/bin/docker-backup.sh
sudo crontab -e

Agrega la línea para correr todos los días a las 3 de la mañana:

0 3 * * * /usr/local/bin/docker-backup.sh >> /var/log/docker-backup.log 2>&1

Redirigir stderr y stdout al log permite diagnóstico cuando algo falla — el silencio en cron es el peor escenario.

Off-site es obligatorio

Backup en el mismo disco del servidor protege contra error humano (docker volume rm), no contra fallo de hardware. Agrega un paso de upload a S3-compatible (mc, rclone) o rsync a otro servidor al final del script. Sin off-site, tu plan de recuperación tiene punto único de fallo.

Verificación y restauración

Un backup que nunca fue probado no es backup — es un archivo. Valida la restauración periódicamente, idealmente en VPS de staging.

Para restaurar un volumen de datos estáticos:

# Crea volumen vacio
docker volume create uploads_restored

# Restaura contenido
docker run --rm \
  -v uploads_restored:/target \
  -v /var/backups/docker-volumes:/backup \
  alpine \
  sh -c 'cd /target && tar xzf /backup/uploads-20260529-030000.tar.gz'

Para restaurar MySQL:

gunzip -c /var/backups/docker-volumes/mysql-20260529-030000.sql.gz \
  | docker exec -i mysql_container mysql -u root -p"$MYSQL_ROOT_PASSWORD"

Después de la restauración, valida integridad — para MySQL, ejecuta CHECK TABLE en las tablas principales; para datos de aplicación, levanta un contenedor apuntando al volumen restaurado y verifica funcionalidad.

Resolución de problemas

”Cannot create container: volume in use”

Aparece cuando intentas eliminar o renombrar un volumen montado en un contenedor activo. Detén el contenedor primero con docker stop antes de manipular el volumen. El backup con :ro monta el volumen en paralelo sin conflicto.

Tarball corrupto durante la restauración

Síntoma: tar: Unexpected EOF in archive. La causa común es disco lleno durante el backup — el archivo fue truncado. Siempre monitorea el espacio libre (df -h) antes de correr backup, y agrega verificación en el script: tar tzf archivo.tar.gz > /dev/null && echo OK.

Backup lento incluso en SSD

Verifica si está siendo escrito en el mismo disco del volumen original — la competición de I/O reduce el throughput a la mitad. Backup hacia disco separado (block storage adicional o red) acelera significativamente. En VPS Hostini, puedes agregar volumen block separado desde el panel para dedicarlo a la zona de backup.

Próximos pasos

Con el backup configurado y validado, considera los próximos niveles de madurez: implementar backup incremental con restic o borg para volúmenes grandes (reduce tiempo y espacio en ~80%), configurar alertas en el script para notificar fallos vía webhook o email, y separar backups por ventana (diario 14 días, semanal 8 semanas, mensual 12 meses) para cubrir escenarios diferentes de recuperación.

Si estás llevando esto a producción, una VPS Hostini ya viene con SSD NVMe y opción de volumen block adicional dedicado a backup, lo que elimina la competición de I/O entre datos activos y copia de seguridad. Para workloads críticos, considera también replicación síncrona a otra región — el backup resuelve pérdida de datos, la replicación resuelve indisponibilidad.

Preguntas frecuentes

¿Puedo hacer backup de un volumen Docker con el contenedor en ejecución?

Sí, para datos estáticos (uploads, configs) el tar directo del volumen funciona con el contenedor activo. Para bases de datos (MySQL, PostgreSQL, MongoDB), necesitas usar el dump nativo (mysqldump, pg_dump) o detener el contenedor brevemente — copiar archivos binarios abiertos por el motor de base de datos resulta en un backup corrupto.

¿Cuál es la diferencia entre backup de volumen y backup de contenedor?

El contenedor es efímero — la imagen más el Dockerfile lo reconstruyen. El volumen es el estado persistente que necesita ser preservado. El backup de contenedor rara vez tiene sentido; el backup de volumen es lo que importa para recuperar datos reales después de un fallo.

¿Dónde almacenar los backups para que estén seguros?

Nunca en el mismo disco del servidor — si el disco falla, lo perdiste todo junto. Usa almacenamiento remoto (S3-compatible, rsync a otro servidor, o storage block externo). La regla 3-2-1 aplica aquí: 3 copias, 2 medios diferentes, 1 off-site.

¿Cuánto tarda un backup full de 50 GB?

Depende de la compresión y del disco. Con tar + gzip en SSD NVMe, espera 8-15 minutos para 50 GB; con tar puro (sin compresión) baja a 3-5 minutos pero el archivo queda más grande. Para volúmenes grandes, considera backup incremental con restic o borg.

¿Cómo restaurar un volumen a partir del tarball?

Detén el contenedor, crea el volumen vacío con docker volume create, móntalo en un contenedor temporal (alpine) con el tarball accesible, ejecuta tar xzf dentro del mount point, y levanta el contenedor original. El comando exacto está en la sección de verificación.

¿Necesito detener el daemon Docker entero para hacer backup?

No. Detén solo los contenedores que usan los volúmenes específicos. El daemon necesita seguir ejecutándose para que puedas ejecutar comandos docker durante el backup. Detener el daemon entero afecta contenedores no relacionados innecesariamente.

Temas:
Próximos pasos Cloud Ryzen con NVMe y protección DDoS siempre activa.Pon en producción en un VPS Hostini →
¿Te resultó útil este tutorial?
Hablar por WhatsApp