Tutorial: migrar um host Docker em produção para outra VPS com mínima indisponibilidade usando rsync, volumes e Docker Compose
Deploy & DevOps · Por Kevin Marcondes · Publicado em · 9 min de leitura
Tutorial: migrar um host Docker em produção para outra VPS com mínima indisponibilidade usando rsync, volumes e Docker Compose. Você fará uma sincronização em duas etapas, validará o destino e trocará o tráfego por DNS ou proxy. O foco é reduzir o downtime sem arriscar a integridade dos dados.
Antes de começar: pré-requisitos e janela de corte
Planeje uma janela curta para o corte final. Reduza o TTL dos registros DNS horas antes. Faça backup do que é crítico antes de mexer em dados. Tenha acesso sudo/root em ambas as VPS e autenticação por chave SSH.
Cheque espaço em disco no destino e a compatibilidade de arquitetura (amd64/arm64) das imagens. Prepare uma lista do que será migrado: Compose files, variáveis de ambiente, secrets e volumes persistentes.
Se precisar reforçar a base do servidor de destino, confira o checklist de segurança para VPS Linux.
- Acesso SSH com chave a ambas as VPS
- Sudo/root para manipular volumes em /var/lib/docker/volumes
- TTL do DNS reduzido previamente
- Backup recente dos dados críticos
- Mesma arquitetura ou imagens multi-arquitetura
Mapeie serviços, volumes e portas no host atual
Faça um inventário do que roda hoje. Anote nomes dos serviços, portas expostas, redes e volumes. Isso evita surpresas no corte. Os comandos abaixo ajudam a extrair rapidamente a visão do ambiente atual.
- Confirme quais volumes são stateful (banco, uploads, filas)
- Revise healthchecks e dependências entre serviços
- Liste variáveis sensíveis que não estão em .env (ex.: secrets externos)
# No host de origem
# Projetos/stack e containers em execução
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
# Volumes Docker
docker volume ls
# Mapas de montagem por container (mostra bind mounts e volumes)
docker inspect -f '{{ .Name }} -> {{range .Mounts}}{{.Type}}:{{.Source}}:{{.Destination}} {{end}}' $(docker ps -q)
Prepare a VPS de destino (usuário, firewall, Docker/Compose)
Crie um usuário para deploy, habilite firewall e instale Docker + Compose plugin. Reaplique sysctls ou limites usados no host atual. Se preferir um guia dedicado de instalação, veja como instalar Docker e Docker Compose no Linux.
- Separe um diretório para projetos (ex.: /opt/projetos)
- Habilite UFW apenas nas portas necessárias
- Garanta versão mínima compatível de Compose
# Na VPS de destino (Debian/Ubuntu)
sudo apt-get update
sudo apt-get install -y docker.io docker-compose-plugin rsync ufw
# Usuário de deploy e acesso ao Docker
sudo useradd -m -s /bin/bash deploy || true
sudo usermod -aG docker deploy
# Firewall básico
sudo ufw allow OpenSSH
# Abra apenas o que seu stack realmente usa (exemplo: HTTP/HTTPS)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Sincronize Compose, variáveis e arquivos de configuração
Copie o repositório do projeto, arquivos docker-compose*.yml, .env e quaisquer overrides para o destino. Não copie docker.sock nem arquivos temporários. Mantenha a mesma estrutura de diretórios para evitar ajustes desnecessários nos paths.
- Use --exclude para ignorar diretórios grandes (ex.: node_modules)
- Confirme permissões dos arquivos .env e secrets no destino
- Evite credenciais hardcoded; mantenha .env separado
# Do host de origem para o destino (ajuste caminhos/host)
DEST="deploy@IP_OU_HOST_DESTINO"
PROJ="/opt/meu-stack"
rsync -avz --progress -e ssh \
--exclude '.git' --exclude 'node_modules' \
${PROJ}/ ${DEST}:${PROJ}/
# Se .env não estiver no repo
rsync -avz -e ssh ${PROJ}/.env ${DEST}:${PROJ}/.env
Primeira sincronização dos volumes (ambiente rodando)
Faremos duas passagens com rsync: uma a quente (app ainda atendendo) e outra rápida durante a janela de corte. Para bancos de dados, prefira replica/dump; rsync direto do _data pode corromper se houver escrita. Para volumes de uploads e logs, rsync funciona bem.
- Crie os volumes no destino antes de copiar
- Use --numeric-ids para preservar UIDs/GIDs
- Evite sincronizar sockets e arquivos em uso; foque no _data
| Cenário | Estratégia recomendada | Risco de inconsistência | Observações |
|---|---|---|---|
| Uploads/arquivos estáticos | Rsync em duas passagens | Baixo | Parar app apenas na sincronização final |
| Banco relacional (PostgreSQL/MySQL) | Replica/backup+restore | Alto se via rsync | Use dump/replicação; rsync só com serviço parado |
| Caches/filas (Redis/RabbitMQ) | Recriar vazio | Baixo | Perda aceitável de cache; esvazie filas antes |
| Bind mounts de config | Rsync simples | Baixo | Garanta paths idênticos em Compose |
# Do host de origem: crie e prepare volumes no destino, então rsync "quente"
DEST="deploy@IP_OU_HOST_DESTINO"
VOLS=(app_uploads db_data redis_data)
ssh -o StrictHostKeyChecking=no ${DEST} 'for v in app_uploads db_data redis_data; do sudo docker volume create $v >/dev/null; sudo mkdir -p /var/lib/docker/volumes/$v/_data; done'
for v in "${VOLS[@]}"; do
sudo rsync -aHAX --numeric-ids --info=progress2 \
-e ssh /var/lib/docker/volumes/${v}/_data/ \
${DEST}:/var/lib/docker/volumes/${v}/_data/
done
Teste no destino sem expor tráfego público
Suba o stack no destino e valide com curl e healthchecks em localhost ou via túnel SSH. Não aponte DNS ainda. Se usar portas padrão, mantenha firewall fechado externamente enquanto testa. Ajuste variáveis específicas do ambiente no .env do destino.
- Teste dependências: conexão a banco, filas e storage externo
- Confirme redes e nomes de host internos do Compose
- Garanta que imagens corretas foram baixadas
# Na VPS de destino, no diretório do projeto
cd /opt/meu-stack
sudo docker compose --env-file .env pull
sudo docker compose --env-file .env up -d
sudo docker compose ps
# Testes locais
curl -fsS http://127.0.0.1:80/health || true
sudo docker compose logs --no-color --tail=50
Janela de corte: parar escrita, rsync final e troca de DNS
Agora faremos o corte. Este é o único momento de indisponibilidade planejada. Pare os serviços na origem, faça um rsync final com --delete e suba no destino. Cuidado: --delete apaga arquivos no destino que não existirem na origem. Em seguida, troque o DNS e aguarde a propagação conforme o TTL.
- Avise usuários e monitore métricas em tempo real
- Tenha rollback simples: manter origem intacta até validar
- Atualize tokens e allowlists que apontem por IP
# 1) Parar serviços na ORIGEM
cd /opt/meu-stack
sudo docker compose --env-file .env down
# 2) Sincronização FINAL dos volumes (com --delete)
DEST="deploy@IP_OU_HOST_DESTINO"
for v in app_uploads db_data redis_data; do
sudo rsync -aHAX --numeric-ids --delete --info=progress2 \
-e ssh /var/lib/docker/volumes/${v}/_data/ \
${DEST}:/var/lib/docker/volumes/${v}/_data/
done
# 3) Subir no DESTINO
ssh ${DEST} 'cd /opt/meu-stack && sudo docker compose --env-file .env up -d && sudo docker compose ps'
# 4) Trocar DNS (exemplo: verifique resolução)
# Altere o A/AAAA no provedor e valide depois
# (ajuste seu domínio)
dig +short A exemplo.com
Como confirmar que funcionou (checks pós-corte)
Valide aplicação, métricas e logs. Confira que nenhum container está reiniciando em loop. Monitore erros 4xx e 5xx. Não desative o host antigo até concluir validações e ter um backup pós-migração.
- Abra a aplicação por HTTPS e verifique login e fluxos críticos
- Confirme integridade de uploads e permissões de arquivos
- Cheque métricas de CPU/RAM, filas e jobs agendados
# Na VPS de destino
sudo docker compose ps
sudo docker compose logs --no-color --tail=100 | sed -n '1,100p'
# Endpoints de saúde e páginas críticas (ajuste URLs)
curl -I https://exemplo.com/health
curl -I https://exemplo.com/
Opções avançadas para reduzir ainda mais o downtime
Se precisa de troca quase instantânea, coloque um proxy na frente apontando para origem e destino e mude o tráfego por healthcheck. Veja o passo a passo de HAProxy com Let's Encrypt e healthchecks.
Para orquestrar releases paralelos por stack, use a estratégia de deploy blue-green com Docker Compose. Para acelerar downloads de imagens durante o corte, um registry Docker privado/mirror ajuda muito.
- Proxy com healthchecks para cutover rápido
- Blue/green em Compose para alternância segura
- Registry/mirror para puxar imagens localmente
Solução de problemas comuns
Alguns erros aparecem com frequência após migrações. A lista abaixo traz sintomas e correções usuais. Cuidado com permissões: UIDs/GIDs diferentes entre hosts quebram acessos a arquivos montados nos containers.
- Permission denied em uploads: ajuste dono do _data (chown) conforme UID do processo no container.
- Containers reiniciando: verifique variáveis obrigatórias no .env e ordem de dependências.
- Arquitetura diferente (amd64/arm64): use imagens multi-arch e recrie containers.
- Portas fechadas: revise UFW/security groups e confirme regras de entrada.
- Pulls lentos no corte: configure um mirror de registry local.
- Banco corrompido após rsync: restaure de backup ou repita usando dump/replicação com serviço parado.
Perguntas frequentes
Como migrar um banco de dados Docker com segurança?
Para bancos relacionais, use réplica temporária ou dump e restore. Evite rsync do diretório _data com o serviço escrevendo. Faça a primeira cópia com a instância em read-only ou parada e valide a integridade antes do corte final.
É possível migrar sem downtime?
Sem replicação ou proxy, haverá uma janela curta. Para reduzir quase a zero, use um proxy com healthchecks, prepare o destino, sincronize dados, congele a escrita no corte e mude o tráfego de forma atômica. Ainda assim, planeje rollback.
E se a nova VPS for de outra arquitetura (arm64 vs amd64)?
Certifique-se de que as imagens tenham manifestos multi-arquitetura. Recrie containers no destino. Volumes de dados não dependem da arquitetura, mas os binários dentro das imagens sim. Teste no destino antes do corte.
Como lidar com variáveis de ambiente e secrets?
Armazene .env fora do repositório ou criptografado. Sincronize separadamente e verifique permissões. Secrets externos (tokens, chaves) devem ser revisados após a troca de IP ou DNS para evitar bloqueios por allowlists.
Preciso baixar todas as imagens novamente?
Não necessariamente. Você pode usar um registry privado ou mirror para acelerar pulls no destino. Outra opção é usar docker save e docker load, mas costuma ser mais simples puxar as imagens versionadas pelo Compose no novo host.
Quer revisar seu plano de migração antes do corte?
Se você precisa de uma segunda opinião sobre o desenho do corte, volumes críticos e rollback, fale comigo e leve um checklist sob medida para seu stack.
Conversar sobre a migraçãoVeja 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