Backup de Volumes Docker em VPS: Guia Prático de Persistência
Aprenda a fazer backup de volumes Docker em VPS sem parar containers. Estratégias para bancos de dados, configs e dados persistentes em produção.
Volumes Docker são o ponto onde a abstração de container encontra a realidade do disco. Container é descartável — você reconstrói a partir da imagem em minutos. Volume é onde mora o estado real: banco de dados, uploads de usuários, configurações persistentes. Perder um volume é perder dados de produção.
Apesar disso, backup de volumes é a parte mais negligenciada do stack Docker. A maioria das VPS rodando aplicações containerizadas não tem rotina de backup configurada, apenas a falsa segurança de que “está tudo num container”. Quando o disco falha ou alguém roda docker volume prune distraído, a perda é definitiva.
Este tutorial mostra estratégias práticas pra fazer backup de volumes Docker em VPS Linux, cobrindo dados estáticos, bancos de dados, e automação com cron. Tempo estimado de execução: 25-40 minutos pra configurar o primeiro backup e validar a restauração.
Pré-requisitos
VPS rodando Ubuntu 22.04 LTS ou Debian 12 com Docker Engine 24+ instalado, pelo menos um volume Docker em uso por algum container, e acesso sudo. Espaço livre equivalente a 1.5x o tamanho do volume original (pra compressão temporária) e, idealmente, destino remoto pros backups (S3-compatível, outro servidor, ou storage externo).
Confirme a versão do Docker e liste seus volumes antes de começar:
docker --version
docker volume ls
24.x ou superior Ubuntu 22.04+ / Debian 12+ 1.5x tamanho do volume S3, rsync ou block storage Identificando volumes e containers afetados
Antes de fazer backup, mapeie quais containers usam cada volume. Backup cego sem entender as dependências causa inconsistência — você pode pegar o arquivo de banco no meio de uma transação se o container estiver gravando.
Liste todos os volumes nomeados e os containers que os utilizam:
docker volume ls --format '{{.Name}}'Pra cada volume, identifique os containers consumidores:
docker ps -a --filter volume=nome_do_volume --format '{{.Names}}: {{.Image}}'Volumes anônimos (com hash longo) geralmente são descartáveis — vêm de imagens que declaram VOLUME no Dockerfile sem nome explícito. Foque nos volumes nomeados.
Inspecione o ponto de montagem físico de cada volume:
docker volume inspect nome_do_volumeO campo Mountpoint mostra onde o Docker armazena os dados — tipicamente /var/lib/docker/volumes/<nome>/_data. Esse path é o que você precisa pra backup direto via tar.
Verifique o tamanho real de cada volume:
sudo du -sh /var/lib/docker/volumes/nome_do_volume/_dataUse esse valor pra dimensionar espaço de destino e estimar tempo de backup. Volumes acima de 20 GB merecem estratégia incremental.
Backup de dados estáticos com tar
Dados estáticos — uploads de usuário, configs, assets — podem ser copiados com o container rodando. O risco de inconsistência é baixo porque escritas atômicas (mv, rename) preservam o estado anterior até a operação completar.
A estratégia padrão usa um container temporário Alpine com o volume montado em modo somente-leitura, criando o tarball pra um diretório do host.
Crie um diretório pros backups no host:
sudo mkdir -p /var/backups/docker-volumes
sudo chmod 700 /var/backups/docker-volumesPermissão 700 garante que apenas root leia os arquivos — backups frequentemente contêm dados sensíveis.
Execute o backup de um volume com container Alpine descartável:
docker run --rm \
-v nome_do_volume:/source:ro \
-v /var/backups/docker-volumes:/backup \
alpine \
tar czf /backup/volume-$(date +%Y%m%d-%H%M%S).tar.gz -C /source .O flag :ro monta o volume em somente-leitura — impede que o container temporário modifique dados acidentalmente. O -C /source . evita incluir o path /source na estrutura do tarball, facilitando restauração.
Compressão gzip é single-threaded — pra volumes acima de 10 GB, considere pigz (gzip paralelo) ou zstd que escalam com cores disponíveis. Em VPS de 2 vCPUs, gzip puro é aceitável; em 8+ vCPUs, pigz reduz o tempo pela metade.
Backup consistente de bancos de dados
Bancos de dados são o caso mais delicado. Copiar os arquivos binários (datadir do MySQL, base/ do PostgreSQL) com o engine rodando resulta em backup corrompido — escritas em andamento, WAL não-flushed, índices inconsistentes. A solução é usar o dump nativo do engine.
Pra MySQL/MariaDB, execute mysqldump direto do container:
docker exec nome_container_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.gzA flag --single-transaction cria um snapshot consistente em InnoDB sem bloquear escritas — funciona porque o dump roda dentro de uma transação isolada. Pra tabelas MyISAM, adicione --lock-tables (mas considere migrar pra InnoDB).
Pra PostgreSQL, use pg_dumpall ou pg_dump por database:
docker exec nome_container_pg \
pg_dumpall -U postgres \
| gzip > /var/backups/docker-volumes/postgres-$(date +%Y%m%d-%H%M%S).sql.gzpg_dumpall inclui roles e configurações de cluster — necessário pra restauração completa. Pra databases individuais grandes, pg_dump -Fc (custom format) permite restauração paralela com pg_restore -j.
Pra MongoDB, use mongodump pra um diretório temporário no container e tar depois:
docker exec nome_container_mongo \
mongodump --archive --gzip \
> /var/backups/docker-volumes/mongo-$(date +%Y%m%d-%H%M%S).archive.gzO formato archive concatena todos os databases num único stream — mais fácil de transferir que o diretório clássico do mongodump.
Os exemplos acima assumem que a senha está em $MYSQL_ROOT_PASSWORD dentro do container. Nunca passe senha em argumento -p$senha direto no comando — fica visível em ps aux e nos logs do shell. Use sempre variável de ambiente ou arquivo .my.cnf montado.
Automação com cron
Backup manual não escala — você esquece, ou faz só quando lembra (geralmente quando precisa restaurar). Cron resolve isso. A receita abaixo cria um script encapsulando a rotina e agenda execução diária.
Crie um script em /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 volume estatico
docker run --rm \
-v uploads_volume:/source:ro \
-v "$BACKUP_DIR":/backup \
alpine \
tar czf "/backup/uploads-$TIMESTAMP.tar.gz" -C /source .
# Limpa backups antigos
find "$BACKUP_DIR" -name "*.gz" -mtime +$RETENTION_DAYS -deleteO set -euo pipefail aborta em qualquer erro — backup parcial é pior que ausência de backup porque dá falsa sensação de segurança.
Torne o script executável e agende no cron do root:
sudo chmod 700 /usr/local/bin/docker-backup.sh
sudo crontab -eAdicione a linha pra rodar todo dia às 3h da manhã:
0 3 * * * /usr/local/bin/docker-backup.sh >> /var/log/docker-backup.log 2>&1Redirecionar stderr e stdout pro log permite diagnóstico quando algo falha — silêncio no cron é o pior cenário.
Backup no mesmo disco do servidor protege contra erro humano (docker volume rm), não contra falha de hardware. Adicione um passo de upload pra S3-compatível (mc, rclone) ou rsync pra outro servidor no fim do script. Sem off-site, seu plano de recuperação tem ponto único de falha.
Verificação e restauração
Backup que nunca foi testado não é backup — é arquivo. Valide a restauração periodicamente, idealmente em VPS de staging.
Pra restaurar um volume de dados estáticos:
# Cria volume vazio
docker volume create uploads_restored
# Restaura conteudo
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'
Pra 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"
Após restauração, valide integridade — pra MySQL, rode CHECK TABLE nas tabelas principais; pra dados de aplicação, suba um container apontando pro volume restaurado e verifique funcionalidade.
Resolução de problemas
”Cannot create container: volume in use”
Aparece quando você tenta deletar ou renomear um volume montado em container ativo. Pare o container primeiro com docker stop antes de manipular o volume. Backup com :ro monta o volume em paralelo sem conflito.
Tarball corrompido durante restauração
Sintoma: tar: Unexpected EOF in archive. Causa comum é disco cheio durante backup — o arquivo foi truncado. Sempre monitore espaço livre (df -h) antes de rodar backup, e adicione verificação no script: tar tzf arquivo.tar.gz > /dev/null && echo OK.
Backup lento mesmo em SSD
Verifique se está sendo escrito no mesmo disco do volume original — competição de I/O reduz throughput pela metade. Backup pra disco separado (block storage adicional ou rede) acelera significativamente. Em VPS Hostini, você pode adicionar volume block separado pelo painel pra dedicar à zona de backup.
Próximos passos
Com backup configurado e validado, considere os próximos níveis de maturidade: implementar backup incremental com restic ou borg pra volumes grandes (reduz tempo e espaço em ~80%), configurar alertas no script pra notificar falha via webhook ou email, e separar backups por janela (diário 14 dias, semanal 8 semanas, mensal 12 meses) pra cobrir cenários diferentes de recuperação.
Se você está colocando isso em produção, uma VPS Hostini já vem com SSD NVMe e opção de volume block adicional dedicado a backup, o que elimina a competição de I/O entre dados ativos e cópia de segurança. Pra workloads críticos, considere também replicação síncrona pra outra região — backup resolve perda de dados, replicação resolve indisponibilidade.
Perguntas frequentes
Posso fazer backup de um volume Docker com o container rodando?
Sim, pra dados estáticos (uploads, configs) o tar direto do volume funciona com container ativo. Pra bancos de dados (MySQL, PostgreSQL, MongoDB), você precisa usar o dump nativo (mysqldump, pg_dump) ou parar o container brevemente — copiar arquivos binários abertos pelo engine de banco resulta em backup corrompido.
Qual a diferença entre backup de volume e backup de container?
Container é efêmero — a imagem mais o Dockerfile reconstroem ele. Volume é o estado persistente que precisa ser preservado. Backup de container raramente faz sentido; backup de volume é o que importa pra recuperar dados reais após falha.
Onde armazenar os backups pra ficarem seguros?
Nunca no mesmo disco do servidor — se o disco falhar, perdeu tudo junto. Use armazenamento remoto (S3-compatível, rsync pra outro servidor, ou storage block externo). A regra 3-2-1 vale aqui: 3 cópias, 2 mídias diferentes, 1 off-site.
Quanto tempo um backup full de 50 GB demora?
Depende da compressão e do disco. Com tar + gzip em SSD NVMe, espere 8-15 minutos pra 50 GB; com tar puro (sem compressão) cai pra 3-5 minutos mas o arquivo fica maior. Pra volumes grandes, considere backup incremental com restic ou borg.
Como restaurar um volume a partir do tarball?
Pare o container, crie o volume vazio com docker volume create, monte ele em um container temporário (alpine) com o tarball acessível, rode tar xzf dentro do mount point, e suba o container original. O comando exato está na seção de verificação.
Preciso parar o Docker daemon inteiro pra fazer backup?
Não. Pare apenas os containers que usam os volumes específicos. O daemon precisa continuar rodando pra você executar comandos docker durante o backup. Parar o daemon inteiro afeta containers não relacionados desnecessariamente.