tutorial: mover volumes Docker grandes para outro disco no mesmo host com mínima indisponibilidade (rsync + bind‑mount + Docker Compose)
Docker & Containers · Por Kevin Marcondes · Publicado em · 8 min de leitura
Precisa de um tutorial para mover volumes Docker grandes para outro disco no mesmo host com mínima indisponibilidade (rsync + bind‑mount + Docker Compose)? Aqui vai um passo a passo seguro: cópia inicial a quente com rsync, parada curta, sincronização final com --delete e atualização do Docker Compose para bind‑mount no novo disco.
Pré‑requisitos, alertas e cenário
Você vai copiar dados de volumes existentes para um novo disco montado localmente e reconfigurar o Docker Compose para usar bind‑mount. Faça backup antes e planeje uma janela curta de parada. Comandos exigem root ou sudo.
Se quer um plano de backup com snapshots e uploads incrementais, veja este guia: LVM snapshots + Restic em S3. Se o objetivo for migrar para outra VPS, use este passo a passo: migrar um host Docker para outra VPS.
- Novo disco já montado, por exemplo, em /mnt/docker-data
- Docker e Docker Compose v2 disponíveis (comando: docker compose)
- Janela curta de parada para sincronização 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 e caminhos atuais
Descubra quais volumes o projeto usa e onde ficam no host. Ajuste PROJECT ao prefixo do seu compose. Normalmente é o nome do diretório.
Anote o Mountpoint de cada volume. É de onde os dados serão copiados para o novo 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 o destino no novo disco
Crie a estrutura de diretórios no novo disco. Usaremos DEST como caminho base. O rsync preservará donos, ACLs e links.
Você pode ajustar nomes ou agrupar por projeto para manter organização 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) Primeira sincronização a quente com rsync
A primeira cópia ocorre com os containers em execução para transferir o grosso dos dados. Mudanças feitas durante esta etapa serão refletidas depois, na sincronização final com os serviços parados.
- -a: preserva atributos
- -HAX: preserva hardlinks, ACLs e xattrs (se existirem)
- --numeric-ids: mantém UIDs e GIDs exatos
- --info=progress2: mostra progresso contínuo
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) Janela curta: parar serviços e sincronização final
Agora faremos a parada breve para consistência e a sincronização final com --delete. Bancos de dados e serviços com escrita ativa precisam estar parados para garantir integridade.
Você pode parar apenas serviços afetados ou todo o projeto, conforme dependências.
- Use stop para uma parada breve
- --delete remove arquivos apagados na origem
- Mantenha o diretório antigo intocado por enquanto (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) Atualizar o Docker Compose para bind‑mount
Para manter serviços inalterados, redefina os volumes nomeados com driver local e bind para o novo caminho. Assim, onde o compose referia myapp_xxx-data, agora ele monta o diretório no novo disco.
Alternativa: substituir no serviço por binds de caminho (source: /mnt/... target: /path). Se usa SELinux, avalie rotular o path ou usar opções de bind compatíveis.
# 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) Subir serviços e validar montagem no novo disco
Suba os serviços e confirme que os mounts apontam para o novo disco. Em seguida, valide que aplicações e bancos iniciam corretamente.
Se preferir uma interface gráfica para inspeção, veja: como usar o 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) Como confirmar que funcionou e resolver problemas comuns
Faça um teste de escrita em um diretório montado e veja se o arquivo aparece no novo disco. Se algo falhar, abaixo estão problemas típicos e correções.
Para segurança geral do servidor, revise este checklist de segurança de VPS.
- Permissão negada: descubra o UID:GID esperado no container e ajuste com chown no destino.
- Serviço reiniciando em loop: verifique logs com docker compose logs -f e paths configurados.
- Docker rootless: a raiz de volumes muda; use docker info para localizar e ajustar comandos.
- Volumes grandes e lentidão: rode a primeira cópia em horários de baixa carga e limite I/O do rsync.
- Banco de dados: faça a sincronização final com o serviço parado e valide integridade depois.
# 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) Limpeza e rollback seguro
Antes de remover o conteúdo antigo em /var/lib/docker/volumes, rode alguns dias em produção e monitore. Cuidado: excluir dados é definitivo.
Para rollback, basta reverter o compose aos volumes antigos e subir novamente. Mantendo um tar do conteúdo antigo, você tem um plano B simples.
- Mantenha backup do estado antigo por um período combinado.
- Remova apenas depois de validar integrações e relatórios.
- Rollback: retorne o compose original e suba os serviços.
# 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_*
Perguntas frequentes
É possível mover volumes Docker sem nenhum downtime?
Você consegue reduzir muito a indisponibilidade fazendo uma primeira cópia a quente e uma sincronização final curta com os serviços parados. Zero absoluto não é realista em serviços que escrevem ativamente, como bancos de dados.
É seguro para bancos de dados como PostgreSQL e MySQL?
Sim, desde que a sincronização final ocorra com o serviço de banco parado. A cópia inicial agiliza a transferência. A segunda, com --delete e serviços parados, garante consistência. Após subir, valide integridade e checks do seu SGBD.
Preciso alterar todos os serviços no docker‑compose.yml?
Não necessariamente. Você pode redefinir volumes nomeados com driver local e driver_opts type=none, o=bind, device=/mnt/... Assim, os serviços continuam usando os mesmos nomes, mas apontam ao novo disco.
Como descubro quais volumes devo mover primeiro?
Liste os volumes do projeto com docker volume ls e filtre pelo prefixo do compose. Depois, use docker volume inspect para ver o Mountpoint. Priorize os que ocupam mais espaço, como uploads, cache persistente e dados de banco.
Uso Docker rootless. Algo muda no processo?
Os caminhos físicos dos volumes mudam e não ficam sob /var/lib/docker. Use docker info para localizar a raiz de dados do rootless e ajuste inspeções e rsync. O fluxo de sincronização e o bind‑mount permanecem os mesmos.
Posso manter volumes nomeados usando outro disco?
Sim. Defina os volumes com driver local e driver_opts (type=none, o=bind, device=/mnt/...). Assim, os serviços seguem referenciando os mesmos nomes, enquanto o armazenamento passa a residir no novo caminho.
Quer validar o plano antes de rodar em produção?
Envie sua lista de serviços e volumes. Podemos revisar riscos, janela de parada e um checklist de validação para o seu ambiente.
Falar sobre a migração dos volumesVeja também
- tutorial: configurar mTLS entre Traefik e serviços em Docker Compose usando smallstep CA e certificados automáticos — passo a passo
- Tutorial: integrar Sentry ao pipeline de deploy com GitHub Actions para upload automático de source maps e criação de issues (PHP e JavaScript) — passo a passo
- Configurar retenção e compactação de logs de containers Docker com journald + logrotate + rsyslog em VPS com pouco disco
- tutorial: configurar GitHub Actions para testes de integração com PostgreSQL em containers efêmeros e rollback automático de migrações — guia prático