tutorial: mover volúmenes Docker grandes a otro disco en el mismo host con mínima indisponibilidad (rsync + bind‑mount + Docker Compose)
Docker & Containers · Por Kevin Marcondes · Publicado el · 9 min de lectura
¿Necesitas un tutorial para mover volúmenes Docker grandes a otro disco en el mismo host con mínima indisponibilidad (rsync + bind‑mount + Docker Compose)? Aquí tienes un paso a paso seguro: copia inicial en caliente con rsync, parada corta, sincronización final con --delete y actualización del Docker Compose para usar bind‑mount en el nuevo disco.
Pré‑requisitos, alertas y escenario
Vas a copiar datos de volúmenes existentes a un nuevo disco montado localmente y reconfigurar Docker Compose para usar bind‑mount. Haz un backup antes y planifica una ventana corta de parada. Los comandos requieren root o sudo.
Si necesitas un plan de backups con snapshots y cargas incrementales, consulta esta guía: Instantáneas LVM + Restic en S3. Si tu objetivo es migrar a otra VPS, sigue este paso a paso: Migrar un host Docker a otra VPS.
- Nuevo disco ya montado, por ejemplo, en /mnt/docker-data
- Docker y Docker Compose v2 disponibles (comando: docker compose)
- Ventana corta de parada para sincronización final
# Verifique espaço e pontos de montagem
sudo df -hT | sort -k6
sudo lsblk -f
# Confirme que o novo disco está montado (ex.: /mnt/docker-data)
mount | grep /mnt/docker-data || echo "Monte o novo disco antes de prosseguir"
1) Mapear volumes y rutas actuales
Descubre qué volúmenes usa el proyecto y dónde están en el host. Ajusta PROJECT al prefijo de tu compose. Normalmente es el nombre del directorio.
Anota el Mountpoint de cada volumen. Es desde donde se copiarán los datos hacia el nuevo disco.
# Defina o prefixo do projeto (veja com `docker compose ps`)
PROJECT=myapp
# Liste volumes do projeto
vols=$(docker volume ls --format '{{.Name}}' | grep "^${PROJECT}_")
echo "$vols" | sed 's/^/volume: /'
# Veja o caminho (Mountpoint) de cada volume
for v in $vols; do
mp=$(docker volume inspect -f '{{.Mountpoint}}' "$v")
printf '%s -> %s\n' "$v" "$mp"
done
2) Preparar el destino en el nuevo disco
Crea la estructura de directorios en el nuevo disco. Usaremos DEST como ruta base. rsync preservará propietarios, ACLs y enlaces.
Puedes ajustar nombres o agrupar por proyecto para mantener una organización clara.
# Caminho base no novo disco
echo "Defina onde o novo disco está montado (ex.: /mnt/docker-data)"
DEST=/mnt/docker-data
sudo mkdir -p "$DEST"
# Crie diretórios correspondentes aos volumes
for v in $vols; do
sudo mkdir -p "$DEST/$v"
done
# Permissões serão preservadas pelo rsync (-aHAX --numeric-ids)
3) Primera sincronización en caliente con rsync
La primera copia se realiza con los contenedores en ejecución para transferir la mayor parte de los datos. Los cambios hechos durante esta etapa se reflejarán después, en la sincronización final con los servicios parados.
- -a: preserva atributos
- -HAX: preserva hardlinks, ACLs y xattrs (si existen)
- --numeric-ids: mantiene UIDs y GIDs exactos
- --info=progress2: muestra progreso continuo
for v in $vols; do
src=$(docker volume inspect -f '{{.Mountpoint}}' "$v")
dst="$DEST/$v"
echo "Sync: $src -> $dst"
sudo rsync -aHAX --numeric-ids --info=progress2 "$src/" "$dst/"
done
4) Ventana corta: parar servicios y sincronización final
Ahora haremos la parada breve para consistencia y la sincronización final con --delete. Bases de datos y servicios con escritura activa deben estar parados para garantizar integridad.
Puedes detener solo los servicios afectados o todo el proyecto, según dependencias.
- Usa stop para una parada breve
- --delete elimina archivos borrados en el origen
- Mantén el directorio antiguo intacto por ahora (rollback)
# Pare os serviços afetados (ou todo o projeto)
# No diretório do compose:
docker compose stop
# Segunda sincronização (curta), agora com os containers parados
for v in $vols; do
src=$(docker volume inspect -f '{{.Mountpoint}}' "$v")
dst="$DEST/$v"
echo "Final sync: $src -> $dst"
sudo rsync -aHAX --numeric-ids --delete --info=progress2 "$src/" "$dst/"
done
5) Actualizar Docker Compose para bind‑mount
Para mantener los servicios sin cambios, redefine los volúmenes nombrados con driver local y bind al nuevo camino. Así, donde el compose refería myapp_xxx-data, ahora montará el directorio en el nuevo disco.
Alternativa: reemplazar en el servicio por binds de ruta (source: /mnt/... target: /path). Si usas SELinux, valora etiquetar la ruta o usar opciones de bind compatibles.
# docker-compose.yml (trecho)
services:
app:
image: your/image
volumes:
- myapp_app-data:/var/www/html/storage
db:
image: postgres:16
volumes:
- myapp_db-data:/var/lib/postgresql/data
volumes:
myapp_app-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/docker-data/myapp_app-data
myapp_db-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/docker-data/myapp_db-data
6) Levantar servicios y validar montaje en el nuevo disco
Sube los servicios y confirma que los mounts apuntan al nuevo disco. Luego, valida que aplicaciones y bases de datos arranquen correctamente.
Si prefieres una interfaz gráfica para inspección, consulta: usar Portainer.
# No diretório do compose
docker compose up -d
# Verifique que os mounts apontam para o novo caminho
# Substitua "app" e "db" pelos nomes reais dos containers
for c in app db; do
docker inspect "$c" -f '{{range .Mounts}}{{.Destination}} -> {{.Source}} ({{.Type}}){{println}}{{end}}'
done
# Confira espaço e uso no novo disco
sudo df -hT /mnt/docker-data
sudo du -sh /mnt/docker-data/* | sort -h
7) Cómo confirmar que funcionó y resolver problemas comunes
Haz una prueba de escritura en un directorio montado y comprueba que el archivo aparezca en el nuevo disco. Si algo falla, a continuación aparecen problemas típicos y correcciones.
Para seguridad general del servidor, revisa este checklist de seguridad de VPS.
- Permiso denegado: averigua el UID:GID esperado en el contenedor y ajusta con chown en el destino.
- Servicio reiniciando en bucle: revisa logs con docker compose logs -f y las rutas configuradas.
- Docker rootless: la raíz de volúmenes cambia; usa docker info para localizar y ajustar comandos.
- Volúmenes grandes y lentitud: ejecuta la primera copia en horario de baja carga y limita I/O de rsync.
- Base de datos: realiza la sincronización final con el servicio parado y valida la integridad después.
# Teste de escrita (ex.: diretório de uploads do app)
docker exec app sh -c 'echo ok > /var/www/html/storage/.rw-test && ls -l /var/www/html/storage/.rw-test'
# Ajustar donos se necessário (ex.: UID 1000:1000)
sudo chown -R 1000:1000 /mnt/docker-data/myapp_app-data
# Logs para diagnóstico
docker compose logs -f --tail=200
8) Limpieza y rollback seguro
Antes de eliminar el contenido antiguo en /var/lib/docker/volumes, deja correr varios días en producción y monitoriza. Cuidado: eliminar datos es definitivo.
Para rollback, simplemente restaura el compose a los volúmenes antiguos y sube de nuevo. Mantener un tar del contenido antiguo te da un plan B sencillo.
- Conserva un backup del estado antiguo por el período acordado.
- Elimina solo después de validar integraciones e informes.
- Rollback: vuelve al compose original y levanta los servicios.
# Backup dos volumes antigos (ajuste o prefixo)
cd /var/lib/docker/volumes
sudo tar -czf /root/backup-old-volumes-$(date +%F).tgz myapp_*
# Remoção definitiva (após validação)
# sudo rm -rf /var/lib/docker/volumes/myapp_*
Preguntas frecuentes
¿Es posible mover volúmenes Docker sin ningún downtime?
Puedes reducir mucho la indisponibilidad haciendo una primera copia en caliente y una sincronización final corta con los servicios parados. Cero absoluto no es realista en servicios que escriben activamente, como bases de datos.
¿Es seguro para bases de datos como PostgreSQL y MySQL?
Sí, siempre que la sincronización final se haga con el servicio de base de datos parado. La copia inicial acelera la transferencia. La segunda, con --delete y servicios detenidos, garantiza consistencia. Tras levantar, valida integridad y checks del SGBD.
¿Necesito cambiar todos los servicios en el docker‑compose.yml?
No necesariamente. Puedes redefinir volúmenes nombrados con driver local y driver_opts type=none, o=bind, device=/mnt/.... Así, los servicios siguen usando los mismos nombres, pero apuntan al nuevo disco.
¿Cómo detecto qué volúmenes mover primero?
Lista los volúmenes del proyecto con docker volume ls y filtra por el prefijo del compose. Luego usa docker volume inspect para ver el Mountpoint. Prioriza los que ocupan más espacio, como uploads, cache persistente y datos de base de datos.
Uso Docker rootless. ¿Cambia algo en el proceso?
Las rutas físicas de los volúmenes cambian y no están bajo /var/lib/docker. Usa docker info para localizar la raíz de datos de rootless y ajusta las inspecciones y los rsync. El flujo de sincronización y el bind‑mount sigue siendo el mismo.
¿Puedo mantener volúmenes nombrados usando otro disco?
Sí. Define los volúmenes con driver local y driver_opts (type=none, o=bind, device=/mnt/...). Así, los servicios seguirán referenciando los mismos nombres mientras el almacenamiento reside en la nueva ruta.
¿Quieres validar el plan antes de ejecutarlo en producción?
Envía tu lista de servicios y volúmenes. Podemos revisar riesgos, ventana de parada y un checklist de validación para tu entorno.
Hablar sobre la migración de volúmenesVer también
- Tutorial: configurar mTLS entre Traefik y servicios en Docker Compose usando smallstep CA y certificados automáticos — paso a paso
- Tutorial: integrar Sentry en el pipeline de deploy con GitHub Actions para subir source maps automáticamente y generar issues (PHP y JavaScript) — paso a paso
- Configurar retención y compresión de logs de contenedores Docker con journald + logrotate + rsyslog en VPS con poco disco
- Tutorial: configurar GitHub Actions para tests de integración con PostgreSQL en contenedores efímeros y rollback automático de migrations — guía práctica