tutorial: mover volumes Docker grandes para outro disco no mesmo host com mínima indisponibilidade (rsync + bind‑mount + Docker Compose)

Docker & Containers · Por · Publicado em · 8 min de leitura

adesivo do pinguim Tux (mascote do Linux) sobre uma bandeja azul
Evoca ambiente Linux, adequado para tutoriais sobre Docker e migração de volumes. Foto: RealToughCandy.com / Pexels

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 volumes

Veja também