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

O que você precisa

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
Docker Engine 24.x ou superior
Sistema Ubuntu 22.04+ / Debian 12+
Espaço livre 1.5x tamanho do volume
Destino remoto 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.

01

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.

02

Inspecione o ponto de montagem físico de cada volume:

docker volume inspect nome_do_volume

O 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.

03

Verifique o tamanho real de cada volume:

sudo du -sh /var/lib/docker/volumes/nome_do_volume/_data

Use 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.

04

Crie um diretório pros backups no host:

sudo mkdir -p /var/backups/docker-volumes
sudo chmod 700 /var/backups/docker-volumes

Permissão 700 garante que apenas root leia os arquivos — backups frequentemente contêm dados sensíveis.

05

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.

Tarballs grandes consomem CPU

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.

06

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.gz

A 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).

07

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.gz

pg_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.

08

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.gz

O formato archive concatena todos os databases num único stream — mais fácil de transferir que o diretório clássico do mongodump.

Senha em variável de ambiente

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.

09

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 -delete

O set -euo pipefail aborta em qualquer erro — backup parcial é pior que ausência de backup porque dá falsa sensação de segurança.

10

Torne o script executável e agende no cron do root:

sudo chmod 700 /usr/local/bin/docker-backup.sh
sudo crontab -e

Adicione a linha pra rodar todo dia às 3h da manhã:

0 3 * * * /usr/local/bin/docker-backup.sh >> /var/log/docker-backup.log 2>&1

Redirecionar stderr e stdout pro log permite diagnóstico quando algo falha — silêncio no cron é o pior cenário.

Off-site é obrigató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.

Tópicos:
Próximos passos Cloud Ryzen com NVMe e proteção DDoS sempre ativa.Coloque em produção numa VPS Hostini →
Esse tutorial foi útil?
Falar no WhatsApp