SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor

Como fazer backup e atualizar uma stack Docker Compose

Veja o que salvar no Docker Compose: arquivo .env, volumes e dump do banco, como validar a restauração e atualizar a stack sem perder dados.

O que um backup de uma stack do Docker Compose deve conter

Um backup de uma stack do Docker Compose deve conter quatro elementos separados. Se qualquer um deles for perdido, a aplicação não volta a funcionar: o ficheiro Compose, o ficheiro .env no mesmo diretório, o conteúdo de todos os volumes e um dump da base de dados criado pelo cliente da própria base de dados. Copiar os ficheiros de uma base de dados enquanto o respetivo contentor está em execução não é um backup. As atualizações usam a mesma lista, com uma regra adicional: faça o backup antes de executar pull, porque as migrações de esquema são concebidas para avançar e a maioria dos projetos não fornece uma forma de voltar atrás.

Tudo o que se segue pressupõe que a stack já foi implementada e que docker compose ps mostra que está em execução. Os exemplos usam um diretório de projeto em /srv/myapp, com serviços denominados app e db. Substitua estes nomes pelos seus. Os comandos permanecem genéricos de propósito, porque as partes importantes, os volumes e a base de dados, funcionam da mesma forma independentemente da aplicação.

Identifique o que a sua stack armazena

cd /srv/myapp
docker compose ps
docker compose config --volumes
docker volume ls --filter label=com.docker.compose.project=myapp

docker compose config --volumes apresenta os nomes curtos dos volumes nomeados declarados pelo seu ficheiro. docker volume ls apresenta os nomes que esses volumes têm efetivamente no disco. As duas listas diferem porque o Compose coloca o nome do projeto como prefixo: um volume escrito como db_data no ficheiro existe como myapp_db_data. Por predefinição, o nome do projeto é o nome do diretório. Por isso, mudar o nome do diretório faz com que a stack aponte para um novo conjunto de volumes vazios e deixa os volumes antigos no local, com todos os seus dados. Todos os comandos abaixo precisam do nome real indicado por docker volume ls.

Os bind mounts não aparecem em nenhuma das listas. No ficheiro Compose, são as entradas que têm um caminho do host à esquerda dos dois pontos, ./config:/app/config. São diretórios normais no host, por isso as ferramentas habituais conseguem aceder-lhes. Os volumes nomeados ficam em /var/lib/docker/volumes/, e docker volume inspect --format '{{.Mountpoint}}' myapp_db_data apresenta o caminho exato de um deles. O tipo de armazenamento usado pela sua stack determina como deve copiá-la. bind mounts e volumes nomeados explica a diferença em detalhe.

Agora organize o que encontrou em dois grupos. Alguns volumes contêm estado que nada consegue recriar: ficheiros carregados, chaves geradas, a própria base de dados e tudo o que um utilizador introduziu na aplicação. Outros contêm dados derivados, como miniaturas e índices de pesquisa, que a aplicação recria automaticamente. Fazer cópias de segurança do segundo grupo consome espaço em disco e aumenta o tempo de restauro, sem trazer benefícios. Um volume de cache do Redis é o exemplo mais claro: perdê-lo apenas torna o primeiro pedido mais lento.

Faça backup do ficheiro Compose e do ficheiro .env

Os dois ficheiros ficam lado a lado no host, e nenhum deles está dentro de um volume. O .env contém a palavra-passe da base de dados, o segredo da aplicação e quaisquer tokens de API. Por isso, é o ficheiro que transforma um conjunto de volumes numa aplicação funcional. Normalmente, também está listado em .gitignore. Isto significa que um plano baseado em "a minha configuração está no git" exclui o único ficheiro que mais importa. Manter segredos num ficheiro env é o padrão correto e cria uma responsabilidade equivalente para o backup.

sudo install -d -m 700 -o "$USER" -g "$(id -gn)" /srv/backups/myapp
cp -a compose.yaml .env /srv/backups/myapp/
chmod 600 /srv/backups/myapp/.env

Copie todos os ficheiros Compose usados pela stack, e não apenas o primeiro. Uma stack iniciada com -f compose.yaml -f compose.prod.yaml precisa dos dois ficheiros para voltar a funcionar da mesma forma. Como vários ficheiros Compose são combinados determina quais valores chegam efetivamente ao contentor.

Existe uma relação importante entre .env e os volumes. A imagem oficial do Postgres lê POSTGRES_PASSWORD apenas quando inicializa um diretório de dados vazio. Alterar esse valor posteriormente não altera a palavra-passe dentro da base de dados. Se restaurar o volume do mês passado juntamente com o .env de hoje, a aplicação falha ao ligar-se com FATAL: password authentication failed for user "appuser", embora ambos os ficheiros pareçam corretos durante a verificação. Mantenha o .env e os volumes correspondentes ao mesmo momento juntos no mesmo backup.

Despejar a base de dados com o próprio cliente

Um servidor de base de dados escreve constantemente nos seus ficheiros. Um tar de /var/lib/postgresql/data feito enquanto o servidor está em execução copia algumas páginas anteriores a uma escrita e outras posteriores. Por isso, o arquivo contém uma mistura de momentos que pode não ser reproduzida. Uma ferramenta de dump lê dentro de uma única transação, pelo que o ficheiro contém um momento consistente. Essa diferença separa uma cópia de segurança de uma cópia simples.

docker compose exec -T db sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
  > /srv/backups/myapp/db-$(date +%F).dump

Mantenha o -T. Esta opção desativa a alocação de TTY. Com um TTY associado, o Docker traduz o fluxo de saída antes de o enviar para a sua shell, o que corrompe um dump binário. Só descobrirá isso quando a restauração falhar. As aspas simples também são importantes. Impedem a shell do host de expandir $POSTGRES_USER, para que a shell dentro do contentor o expanda usando os valores que o ficheiro Compose já define nesse ambiente. -Fc escreve no formato personalizado, que comprime os dados durante a execução e permite que pg_restore selecione objetos posteriormente.

As funções e as respetivas palavras-passe ficam fora de qualquer base de dados individual. Por isso, faça também o dump delas:

docker compose exec -T db sh -c 'pg_dumpall -U "$POSTGRES_USER" --globals-only' \
  > /srv/backups/myapp/globals.sql

Em seguida, confirme que o ficheiro é um dump e não uma mensagem de erro:

ls -lh /srv/backups/myapp/
head -c 5 /srv/backups/myapp/db-$(date +%F).dump

Um dump no formato personalizado começa com os cinco bytes PGDMP. Um ficheiro com zero bytes, ou que comece por pg_dump:, indica que o comando falhou. A shell cria o ficheiro de saída antes de executar o comando. Por isso, um dump falhado deixa um ficheiro com um nome plausível e uma data plausível. Esta é a falha silenciosa de cópia de segurança mais comum.

Para MariaDB ou MySQL, o cliente muda, mas a estrutura mantém-se:

docker compose exec -T db sh -c \
  'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" --single-transaction --databases "$MARIADB_DATABASE"' \
  > /srv/backups/myapp/db-$(date +%F).sql

--single-transaction cria um dump consistente das tabelas InnoDB sem bloquear as escritas. Na imagem MySQL, o comando é mysqldump e as variáveis são MYSQL_ROOT_PASSWORD e MYSQL_DATABASE. Nas imagens MariaDB atuais, mysqldump continua a funcionar como nome de compatibilidade para mariadb-dump. Tenha em atenção que uma palavra-passe fornecida na linha de comandos fica visível na lista de processos do contentor enquanto o dump estiver em execução.

O SQLite exige cuidados específicos. A base de dados é um único ficheiro, mas as transações recentes podem ainda estar num ficheiro -wal separado, junto da base de dados. Copiar apenas o .db deixa a base de dados sem as escritas mais recentes. Se a imagem incluir o cliente, sqlite3 /data/app.db ".backup '/data/app-backup.db'" escreve uma cópia consistente enquanto a aplicação está em execução. Caso contrário, pare o contentor e copie o ficheiro .db juntamente com os ficheiros -wal e -shm associados.

Se a base de dados for executada no host, em vez de ser executada dentro da stack, aplique os mesmos comandos sem o prefixo docker compose exec. Também vale a pena ler executar a base de dados no Docker ou no host antes da próxima reconstrução.

Capture os volumes

Um volume nomeado não tem um caminho no host que deva editar manualmente. Monte-o num contentor temporário e crie o arquivo a partir daí.

docker run --rm \
  -v myapp_uploads:/data:ro \
  -v /srv/backups/myapp:/backup \
  alpine:3 tar czf /backup/uploads.tar.gz -C /data .

O contentor auxiliar monta o volume em modo apenas de leitura em /data e o diretório de backup em /backup. Depois, grava o arquivo no lado do host. --rm remove o contentor auxiliar assim que tar termina. :ro é importante porque um comando tar escrito incorretamente não poderá danificar a origem. -C /data . faz com que o restauro seja colocado no local correto: armazena todos os caminhos relativos à raiz do volume. Se escrever tar czf /backup/uploads.tar.gz /data, todos os caminhos passam a ter data/ no início. Assim, o restauro cria /data/data dentro do volume e a aplicação vê um diretório vazio. O arquivo pertence a root porque o tar foi executado como root dentro do contentor. Execute sudo chown "$USER" /srv/backups/myapp/uploads.tar.gz se isso for um problema e consulte como PUID e PGID determinam a propriedade dos ficheiros se os ficheiros restaurados ficarem ilegíveis para a aplicação.

Execute-o uma vez por cada volume nomeado. Os bind mounts não precisam de nenhum contentor: tar czf /srv/backups/myapp/config.tar.gz -C /srv/myapp/config . faz o mesmo trabalho no host.

Decida, para cada volume, se a aplicação tem de ser parada. Um tar executado enquanto a aplicação reescreve o volume pode capturar um ficheiro a meio de uma escrita. Num diretório de uploads, onde os ficheiros são escritos uma vez e depois apenas lidos, esse risco é pequeno. Para qualquer outro caso, pare o serviço durante a cópia com docker compose stop app e execute depois docker compose start app. stop mantém os contentores e os volumes no lugar, que é exatamente o que pretende neste caso. Consulte a diferença entre down e stop antes de executar qualquer um dos comandos.

Não trate um tar do volume da base de dados como backup da base de dados. O dump é o backup. Um arquivo do volume de uma base de dados parada é um caminho rápido e útil para reconstrução, e nada mais.

A ordem das operações

  1. Copie os ficheiros Compose e o .env para o diretório de backup.
  2. Faça o dump da base de dados enquanto ela ainda está em execução.
  3. Pare o contentor da aplicação se os respetivos volumes forem alterados no local.
  4. Arquive cada volume nomeado e cada diretório de bind mount.
  5. Inicie novamente tudo o que parou e confirme com docker compose ps.
  6. Registe as tags e os digests das imagens usadas pela stack.
  7. Copie todo o diretório de backup para fora deste servidor.

O passo 7 é o que as pessoas deixam para mais tarde.

Retire a cópia do servidor

Um backup no mesmo disco que a stack protege contra os seus próprios erros, mas contra mais nada. Um volume avariado, um servidor eliminado ou uma conta perdida afeta ambas as cópias ao mesmo tempo. Envie o diretório para um armazenamento que não esteja neste VPS, seguindo um agendamento e uma política de retenção. backups restic a partir de um VPS explica a configuração do repositório, as flags de retenção e o comando de verificação, por isso nada disso precisa de ser repetido aqui.

O restic também pode ler o dump diretamente de um pipe. Assim, a base de dados em texto simples nunca é gravada no disco:

docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
  | restic backup --stdin --stdin-filename db.dump

Independentemente da ferramenta utilizada, coloque o agendamento num temporizador do systemd ou numa tarefa do cron e faça o job comunicar as falhas para algum local que irá consultar. Um script de backup cuja saída não chega a lado nenhum pode deixar de funcionar durante seis meses sem que ninguém descubra.

Demonstre a recuperação da cópia de segurança com um teste de restauração

Uma cópia de segurança que nunca foi restaurada é apenas uma hipótese. O teste abaixo restaura-a numa segunda stack, executada ao lado da primeira. Assim, a produção continua a servir pedidos e nada do que escrever pode chegar até ela.

O mecanismo é o nome do projeto. O Compose obtém esse nome a partir do nome do diretório e aplica-o a todos os contentores e volumes que cria. Copie a cópia de segurança para um diretório novo. A stack restaurada obterá automaticamente os seus próprios volumes.

sudo install -d -m 700 -o "$USER" -g "$(id -gn)" /srv/myapp-restore
cd /srv/myapp-restore
cp /srv/backups/myapp/compose.yaml /srv/backups/myapp/.env .

Edite o ficheiro Compose copiado para que a porta publicada no host não entre em conflito com a stack em execução, 18080:8080 em vez de 8080:8080, ou altere a variável que a define no .env copiado. Em seguida, crie os contentores e os respetivos volumes vazios sem iniciar nada:

docker compose create
docker volume ls --filter label=com.docker.compose.project=myapp-restore

O segundo comando deve listar os mesmos nomes de volumes da produção, precedidos por myapp-restore_. Preencha-os, inicie apenas a base de dados e carregue o dump:

docker run --rm -v myapp-restore_uploads:/data -v /srv/backups/myapp:/backup \
  alpine:3 tar xzf /backup/uploads.tar.gz -C /data
docker compose up -d db
docker compose exec -T db sh -c \
  'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists' \
  < /srv/backups/myapp/db-2026-08-16.dump

--clean --if-exists elimina cada objeto antes de o recriar. Isso torna a restauração repetível. Sem essa opção, uma segunda execução numa base de dados que já contenha essas tabelas termina com pg_restore: error: could not execute query: ERROR: relation "users" already exists.

Depois, inicie o restante e verifique-o como faria um utilizador:

docker compose up -d --wait
docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt"'
docker compose logs --tail=50

docker compose up -d --wait espera até que todos os serviços indiquem o estado em execução ou saudável e termina com um código diferente de zero se algum nunca chegar a esse estado. É isso que torna esta etapa adequada para scripts. Quando um serviço nunca fica saudável, docker compose ps mostra o seu estado, e as verificações de saúde do Compose explica o que essa coluna está a apresentar. Em seguida, abra a aplicação na porta alternativa e inicie sessão com uma conta real. Crie um registo e abra um ficheiro armazenado num volume. Esse par de ações é a prova: o dump foi restaurado, o volume foi restaurado e ambos estão consistentes. Um teste que apenas confirme que a página de início de sessão é apresentada não prova nada sobre os seus dados.

Elimine o ambiente de teste depois de o validar:

docker compose down -v

Este é o único local onde -v é a opção correta. No diretório de produção, o mesmo comando elimina os volumes que está a tentar proteger.

Como atualizar uma stack Compose

Leia as notas de versão de todas as versões entre a que está a utilizar e a que pretende instalar. Procure nelas as palavras breaking e migration. Os projetos que não suportam saltar várias versões principais indicam-no aí. Uma migração que não pode ser executada só o informa depois de já ter alterado parte do schema.

Registe o que está a executar agora, antes de alterar qualquer coisa:

docker compose images
docker image inspect --format '{{index .RepoDigests 0}}' postgres:16.4

docker compose images apresenta a imagem e a tag que cada serviço está a utilizar neste momento. O digest é o único valor que identifica uma imagem de forma exata, porque uma tag pode ser alterada para apontar para outro local a qualquer momento.

Utilize o backup das secções anteriores e copie-o para fora do servidor. Faça isto também para uma versão de correção. As atualizações fáceis são aquelas para as quais as pessoas deixam de se preparar.

Em seguida, fixe a versão no ficheiro Compose, porque latest não é uma versão:

services:
  db:
    image: postgres:16.4

Com image: postgres:latest, docker compose pull obtém o que quer que essa tag indique hoje, e não tem forma de identificar o que estava a executar ontem. Uma tag fixa transforma a atualização numa edição de uma linha, que pode consultar em git diff e reverter com outra edição. Fixe a imagem da aplicação da mesma forma, utilizando a versão exata indicada na página de versões do projeto.

Obtenha as imagens e recrie os serviços:

docker compose pull
docker compose up -d --wait

docker compose up -d compara o ficheiro com os contentores em execução e recria apenas os serviços cuja imagem ou configuração foi alterada. Não modifica volumes nomeados, por isso o novo contentor inicia com os dados existentes. Esse é o objetivo do procedimento e também o risco, porque o primeiro arranque da nova versão é normalmente quando é executada a migração do schema.

Acompanhe o processo:

docker compose ps
docker compose logs -f --tail=100 app

Um contentor que falhou apresenta Exited (1) na coluna STATUS de docker compose ps, e o motivo está nas últimas linhas do respetivo log. Os erros de migração são claramente apresentados aí e não são visíveis noutro local. Quando os logs estabilizarem, inicie sessão e utilize a aplicação durante um minuto.

Se docker compose pull parar com no space left on device, as camadas antigas da imagem são normalmente a causa, e a remoção de imagens Docker não utilizadas recupera o espaço. Faça a remoção depois de confirmar que a atualização funciona, não antes, porque essas camadas antigas são utilizadas por um rollback rápido.

Como reverter uma atualização quando algo corre mal

Existem dois casos, com custos muito diferentes. Se a nova versão não alterou o esquema, a reversão requer uma única linha: volte a colocar a tag antiga no ficheiro Compose e execute docker compose up -d. O contentor é substituído, os volumes permanecem no mesmo local e o código antigo consegue ler os dados que escreveu.

Se a nova versão migrou o esquema, o código antigo deixa de conseguir lê-lo. As migrações são concebidas para avançar, e a maioria dos projetos não fornece qualquer script de downgrade. Por isso, a versão antiga inicia e falha na primeira consulta a uma coluna que tenha sido renomeada ou removida, com erros do tipo ERROR: column "avatar_url" does not exist. O caminho de regresso é o dump criado antes do pull: volte a colocar a tag antiga, remova o volume da base de dados, recrie-o vazio, restaure o dump e inicie o serviço. Sem esse dump, não existe qualquer forma de voltar atrás. É precisamente por isso que a cópia de segurança deve ser feita antes do pull.

As versões principais do Postgres são o caso mais crítico. Também surpreendem porque a falha ocorre durante a atualização, e não durante a reversão. O formato em disco muda em cada versão principal. Altere postgres:16.4 para postgres:17.2, execute docker compose up -d e o novo servidor recusa-se a iniciar:

FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.2.

A imagem não executa pg_upgrade por si própria. O procedimento suportado dentro de uma stack Compose é fazer dump, substituir e restaurar: faça o dump com a versão antiga ainda em execução, execute docker compose down, remova o volume da base de dados, defina a nova tag, execute docker compose create para criar um diretório de dados vazio, inicie a base de dados, restaure o dump e inicie os restantes serviços. Mantenha o dump antigo até a nova versão principal ter processado tráfego real durante um dia. As atualizações menores dentro da mesma versão principal, de 16.4 para 16.9, não exigem nada disto, porque o formato permanece estável entre elas e o contentor inicia normalmente.

Os snapshots de VPS são um backup?

São um complemento a um backup, e os dois falham de formas diferentes. Um snapshot copia o disco completo no hypervisor, por isso repõe a máquina inteira em minutos, incluindo as partes que se esqueceu de incluir no backup. É a ferramenta certa para uma situação específica: uma atualização danificou o servidor e pretende restaurá-lo ao estado de há vinte minutos.

É uma ferramenta inadequada para tudo o resto. A granularidade é a máquina inteira. Para recuperar uma tabela eliminada, é necessário restaurar um servidor completo noutro local e extraí-la daí. A retenção costuma ser curta. As cópias normalmente ficam na mesma conta do provider que aloja o servidor. Por isso, a perda da conta elimina o servidor e os snapshots ao mesmo tempo. Além disso, um snapshot de uma máquina em execução captura a base de dados durante uma operação de escrita. Na primeira inicialização, a base de dados executa a recuperação após falha, e qualquer transação ainda em curso é perdida.

Use ambos. O snapshot é o botão de desfazer para uma janela de atualização. O dump é a cópia que sobrevive à eliminação da conta. diferenças entre snapshots e backups explica quais falhas cada um cobre efetivamente. Esse mesmo diretório de backup também transforma migrar uma stack para um novo VPS numa tarefa rotineira, em vez de exigir uma reconstrução baseada na memória.

O que pode correr mal e o que verá

A opção de volumes em down. docker compose down -v remove os volumes nomeados declarados pelo ficheiro, e o Compose confirma isso com uma linha que contém Volume myapp_db_data Removed. Não existe uma forma de anular a operação. O comando simples docker compose down deixa esses volumes intactos. Escreva a forma longa, docker compose down --volumes, para que a opção destrutiva seja uma palavra que terá de introduzir explicitamente.

Um dump sem a cadeia mágica. pg_restore: error: did not find magic string in file header significa que o ficheiro não é um arquivo. A causa habitual é faltar -T em docker compose exec, porque, com um TTY ligado, o stream é convertido a caminho da shell e o dump binário chega danificado. Crie novamente o dump com -T e verifique os primeiros cinco bytes com head -c 5.

Uma palavra-passe que não muda. FATAL: password authentication failed for user "appuser" depois de um restore significa que .env e o diretório de dados vieram de momentos diferentes. A imagem define essa palavra-passe apenas quando cria um diretório de dados vazio, pelo que editar .env posteriormente não altera nada dentro da base de dados. Restaure o .env correspondente ou altere a palavra-passe dentro da base de dados com ALTER USER.

Um segundo volume vazio. O Docker cria um volume conforme necessário, pelo que docker run -v myapp_upload:/data com s em falta escreve num volume novo e vazio e comunica sucesso. docker volume ls mostra depois ambos os nomes, um deles sem dados. Copie os nomes dos volumes de docker volume ls em vez de os escrever de memória.

Um restore dirigido à produção. Executar os comandos de restore em /srv/myapp em vez de /srv/myapp-restore substitui os dados ativos pela cópia de segurança, e os comandos têm o mesmo aspeto nos dois locais. Verifique pwd antes de cada comando de restore e mantenha o exercício no seu próprio diretório.

FAQ

docker compose down elimina os meus dados?

Não. docker compose down remove os contentores e a rede predefinida, mas não altera volumes nomeados nem bind mounts. docker compose down -v remove os volumes nomeados declarados pelo seu ficheiro, e essa operação é permanente. Os bind mounts são diretórios do host, por isso o Compose nunca os remove. Se quiser parar os serviços durante uma cópia de segurança sem alterar o resto, use docker compose stop.

Posso copiar o diretório de dados do Postgres em vez de executar pg_dump?

Apenas com o contentor parado. Enquanto o servidor está em execução, os ficheiros são alterados e a cópia pode conter uma mistura de momentos que não pode ser reproduzida corretamente. Uma cópia ao nível dos ficheiros também fica associada a uma versão major específica do Postgres, por isso não arranca com uma versão diferente. Pare o contentor, arquive o volume, inicie-o novamente e trate o resultado como uma forma rápida de reconstrução, não como a sua única cópia de segurança. O dump é a cópia portátil e aquela a partir da qual deve fazer a restauração.

Como atualizo o Postgres para uma nova versão major no Compose?

Alterar a tag não é suficiente. O novo servidor recusa iniciar com o diretório de dados antigo e regista The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.2. Execute pg_dump com a versão antiga ainda em execução, depois docker compose down, remova o volume da base de dados, defina a nova tag, execute docker compose create para criar um volume vazio, inicie a base de dados e restaure o dump nesse volume. Mantenha o dump antigo até a nova versão ter processado tráfego real.

Com que frequência devo executar cópias de segurança e durante quanto tempo devo mantê-las?

Associe o intervalo à quantidade de trabalho que aceita repetir. Uma cópia noturna é adequada para uma instalação pessoal ou de uma equipa pequena, juntamente com uma cópia manual adicional imediatamente antes de qualquer atualização. Quanto à retenção, mantenha histórico suficiente para abranger danos que não detetou de imediato, porque uma tabela corrompida encontrada na sexta-feira não é protegida pela cópia de quinta-feira à noite. restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune é uma política inicial razoável. Independentemente do calendário, faça uma restauração a partir da cópia uma vez por trimestre. Até fazer isso, não tem cópias de segurança; tem ficheiros.

Tenho de parar toda a stack para fazer uma cópia de segurança?

Normalmente, não. O dump da base de dados é consistente enquanto o servidor está em execução, por isso a base de dados não precisa de indisponibilidade. Os volumes são a verdadeira questão. Se a aplicação apenas adiciona ficheiros, como num diretório de uploads, um arquivo criado com o serviço em execução é suficientemente seguro. Se reescrever ficheiros no local, pare esse serviço durante o tempo necessário para a cópia com docker compose stop app e volte a iniciá-lo depois. Parar a aplicação enquanto a base de dados continua em execução é normalmente a janela segura mais curta que pode obter.