Docker prune: liberar espaço em disco no VPS
Veja se imagens, containers, cache de build ou volumes ocupam o disco do VPS e use o prune certo, evitando apagar dados importantes sem possibilidade de desfazer.
Identifique o que está a ocupar espaço em disco antes de fazer qualquer prune
O Docker ocupa espaço em disco num VPS em quatro áreas: imagens, containers parados, cache de build e volumes locais. Execute docker system df primeiro para descobrir qual delas contém os dados, e depois execute o prune mais específico que os remove. A ordem é importante, porque o último comando deste guia, docker volume prune -a, elimina dados e não existe uma forma de desfazer a operação.
Comece pelo sistema de ficheiros, não pelo Docker.
df -h /
sudo du -xh --max-depth=1 /var/lib/docker | sort -hdf mostra a gravidade do problema. du mostra onde o espaço foi utilizado. A flag -x mantém du no mesmo sistema de ficheiros. Assim, não segue um mount para um volume separado nem contabiliza os mesmos dados duas vezes. Há cinco diretórios relevantes: overlay2 contém as camadas de imagens e containers, volumes contém os dados dos volumes, containers contém os metadados dos containers e os ficheiros de log, buildkit contém a cache de build e image contém os metadados das camadas.
Uma nota sobre sudo e os wildcards da shell, porque isto faz muitas pessoas perderem tempo. /var/lib/docker pertence ao root e não pode ser lido pelo seu utilizador normal, portanto ls /var/lib/docker devolve Permission denied. Um comando como sudo du -sh /var/lib/docker/* também falha, porque a sua shell expande * antes de sudo ser executado, e a shell não consegue ler esse diretório. Todos os comandos abaixo usam find ou --max-depth em vez de um wildcard, precisamente por esse motivo.
Agora, a visão do próprio Docker.
docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 6 12.4GB 8.91GB (71%)
Containers 31 6 1.42GB 1.39GB (97%)
Local Volumes 14 5 6.03GB 4.11GB (68%)
Build Cache 212 0 9.87GB 9.87GBEsses valores vêm de uma máquina e não dizem nada sobre a sua. Analise antes a estrutura dos dados. TOTAL contabiliza os objetos, ACTIVE contabiliza os que estão a ser utilizados neste momento e RECLAIMABLE é a estimativa do Docker sobre o espaço que um prune poderia libertar nessa linha.
Há dois aspetos de RECLAIMABLE que causam problemas. As camadas de imagens partilhadas são contabilizadas uma vez por cada imagem que as utiliza, portanto a linha das imagens normalmente indica mais espaço do que aquele que será libertado. Além disso, o comando nunca inclui ficheiros de log de containers, porque o Docker não trata um ficheiro de log como um objeto que possa ser recuperado. Quando du mostra um diretório muito maior do que o valor indicado por docker system df, a causa são os ficheiros de log. Existe uma secção sobre isso mais abaixo.
Adicione -v para obter a discriminação por objeto.
docker system df -vIsto divide o resumo numa secção para cada tipo de objeto. A secção das imagens adiciona as colunas SHARED SIZE e UNIQUE SIZE, para que possa ver quanto uma única imagem realmente ocupa. A secção dos volumes adiciona uma contagem LINKS, que corresponde ao número de containers ligados a esse volume. Lembre-se de LINKS, porque o valor 0 é o teste completo aplicado pelos comandos de prune dos volumes.
Imagens dangling versus imagens não utilizadas
Estas duas expressões parecem intercambiáveis, mas não são. Os filtros comportam-se de forma diferente porque os objetos são diferentes.
Uma imagem dangling é uma imagem sem tag. Ela aparece como <none> em docker images. Você cria uma a cada rebuild: docker build -t myapp:latest . move a tag myapp:latest para a imagem nova, e a imagem antiga mantém todas as suas layers, mas perde o nome. Nada faz referência a ela, e nada a remove automaticamente.
Uma imagem não utilizada é qualquer imagem, com ou sem tag, à qual nenhum container se refere no momento. Uma postgres:16 que você baixou no mês passado e não está a executar agora não é utilizada, mas não é dangling.
docker image prune # dangling images only
docker image prune -a # every image no container refers toA segunda pergunta primeiro.
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N]Leia atentamente esse prompt. "Associados a elas" significa um objeto container existente, em execução ou parado. Se você executou docker compose down, os containers desapareceram, portanto todas as imagens usadas por esses serviços estão agora não utilizadas, e -a elimina-as todas. Nada é perdido de forma irrecuperável, mas o próximo docker compose up -d baixa ou recompila tudo, o que consome largura de banda e tempo de build num VPS pequeno. Esse é um motivo prático para saber o que docker compose down remove e o que stop deixa em execução antes de fazer prune.
Um filtro exclui as imagens recentes do âmbito da operação.
docker image prune -a --filter "until=240h"Isso remove as imagens não utilizadas criadas há mais de 240 horas (10 dias) e deixa as mais recentes intactas. O valor until aceita uma string de duração Go, como 240h, ou um timestamp absoluto, como 2026-08-01T00:00:00.
O que é a cache de build e por que ela cresce sem limite
O BuildKit é o builder que o Docker usa por predefinição para docker build e docker compose build desde o Docker Engine 23.0. Ele coloca em cache o resultado de cada etapa de cada Dockerfile que executa e mantém essa cache em /var/lib/docker/buildkit. A cache é o motivo pelo qual o segundo build termina em segundos, portanto está a funcionar como previsto. O problema é que, por predefinição, nada expira as entradas antigas. Se fizer build da mesma imagem cinquenta vezes com uma etapa COPY que muda a cada execução, fica com cinquenta conjuntos de camadas.
A build cache não é visível para docker image prune. É um tipo de objeto separado, com o seu próprio comando.
docker builder prune # dangling cache
docker builder prune -a # all unused cache
docker builder prune --filter until=168h # cache untouched for 7 daysNenhum destes comandos afeta as suas imagens ou os seus dados. O único custo de limpar a build cache é o facto de o build seguinte demorar mais uma vez. Num VPS que recria imagens regularmente, Build Cache é muitas vezes a maior linha em docker system df, o que faz dela o elemento de maior dimensão que pode apagar com segurança.
Os comandos de limpeza, ordenados do mais seguro ao mais destrutivo
Percorra esta lista e pare assim que df -h / voltar a apresentar um estado saudável. Cada comando imprime uma linha Total reclaimed space: quando termina.
docker container pruneremove os contentores parados. As respetivas camadas graváveis também são removidas, por isso tudo o que um contentor tiver escrito fora de um volume é eliminado com ele. Os volumes não são alterados.docker image pruneremove apenas as imagens dangling. Este é o comando de imagens mais seguro.docker builder pruneremove a cache de compilação dangling. O custo é uma compilação mais lenta.docker image prune -aremove todas as imagens que não sejam referenciadas por nenhum contentor. O custo é voltar a descarregá-las ou compilá-las novamente.docker system pruneexecuta os três primeiros passos de uma vez e adiciona as redes não utilizadas.docker volume pruneremove os volumes anónimos não utilizados.docker volume prune -aremove os volumes não utilizados, incluindo os nomeados. Este é o comando que elimina bases de dados.
docker system prune indica o seu próprio âmbito antes de ser executado.
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- unused build cache
Are you sure you want to continue? [y/N]Os volumes são omitidos deliberadamente dessa lista. Adicionar --volumes inclui novamente os volumes anónimos no âmbito. Adicionar -a alarga o passo das imagens, passando de imagens dangling para todas as imagens não utilizadas. Executar um docker system prune -a --volumes -f completo num host de produção é uma forma de perder dados ao tentar libertar espaço.
Por que a remoção de volumes elimina a sua base de dados
Esta é a secção que deve ler duas vezes.
Um volume é considerado não utilizado quando nenhum contentor está associado a ele. Esse é o único critério. O Docker não verifica se o volume está vazio, se um ficheiro compose ainda o declara ou se contém a única cópia da sua base de dados. LINKS 0 em docker system df -v significa que pode ser removido, e não significa mais nada.
Agora coloque duas ações normais em sequência. Execute docker compose down para reiniciar uma stack de forma limpa. Esse comando remove os contentores e mantém os volumes nomeados, exatamente como está documentado. O seu volume do Postgres deixou de estar associado a qualquer contentor. Dez minutos depois, execute docker volume prune -a para libertar espaço, e a base de dados desaparece. Ambos os comandos funcionaram corretamente. A sequência destruiu os dados.
Desde o Docker Engine 23.0 (API version 1.42), o comando simples é mais restritivo do que era.
WARNING! This will remove anonymous local volumes not used by at least one container.Um volume anónimo é um volume que o Docker criou por si, normalmente porque uma imagem declara VOLUME e nunca lhe atribuiu um nome. Esses volumes normalmente contêm dados que não pediu para manter. Um volume nomeado, do tipo que escreveu no seu ficheiro compose, só é removido quando adiciona -a. As versões mais antigas do Docker removiam ambos com o comando simples. Por isso, não confie em hábitos formados num servidor que atualizou desde então. A diferença só faz sentido depois de perceber como os volumes nomeados diferem dos bind mounts, porque um bind mount não é um volume do Docker e nenhum comando de prune o irá afetar.
Verifique antes de eliminar. Substitua myapp_pgdata pelo nome do volume que está a verificar.
docker volume ls -f dangling=true
docker volume inspect myapp_pgdata
sudo ls -la /var/lib/docker/volumes/myapp_pgdata/_dataO filtro dangling=true aplicado a um volume significa sem referências, não vazio. Listar _data mostra o que está efetivamente dentro dele. Se encontrar um diretório pgdata ou mysql, pare e faça uma cópia antes de continuar. A mesma destruição ocorre através de docker compose down -v, que remove todos os volumes declarados pelo ficheiro compose sem pedir confirmação.
Um volume é a única coisa num host Docker que uma reconstrução não consegue recriar. Por isso, os dados dos volumes devem ser incluídos num backup restic executado fora do servidor, onde uma flag introduzida incorretamente não consegue alcançá-los.
Quando nada é removido: os ficheiros de log dos containers
Já removeu tudo, docker system df mostra quase nada recuperável e o disco continua cheio. Verifique os logs.
sudo find /var/lib/docker/containers -name '*-json.log' -exec du -h {} + | sort -h | tail -10Cada container escreve a saída padrão e o erro padrão num ficheiro JSON em /var/lib/docker/containers/. Numa instalação predefinida, max-size não está definido, o que significa que não há limite. Assim, um único container preso num ciclo de falhas pode escrever até encher a partição. Nenhum comando de remoção elimina estes ficheiros, porque os containers que os produzem estão em execução e, por definição, não podem ser removidos dessa forma.
Não elimine o ficheiro. Executar rm num ficheiro de log aberto não liberta espaço, porque o daemon do Docker mantém um descritor de ficheiro aberto e o kernel mantém esses blocos alocados até esse descritor ser fechado. df não irá diminuir. Em vez disso, trunque o ficheiro. Isto mantém o mesmo inode e permite que o daemon continue a escrever.
sudo find /var/lib/docker/containers -name '*-json.log' -exec truncate -s 0 {} +
df -h /Esta é uma correção temporária. docker logs para esses containers não devolve agora nada, e os ficheiros começam imediatamente a crescer outra vez. A correção efetiva é configurar a rotação, conforme explicado na secção seguinte.
Meça antes e depois, sempre
Nunca tente adivinhar o resultado de um prune. Faça uma medição, execute um comando e faça outra medição.
df -h /
docker system df
docker builder prune -f --filter until=168h
docker image prune -f
docker system df
df -h /Compare os dois resultados de df. Esse é o único número que determina se o servidor continua a servir. docker system df mostra depois qual linha mudou efetivamente, e cada prune apresenta o seu próprio valor de Total reclaimed space:.
Se df não mudou, mas docker system df indica que foi libertado espaço, um descritor de ficheiro aberto está a manter blocos eliminados, que é o problema do ficheiro de log descrito acima. Se ambos mudaram e o disco fica cheio novamente no prazo de um dia, existe um problema de crescimento e não de limpeza. A solução é configurar a rotação e um job agendado.
Como evitar que o disco volte a ficar cheio
Limite o tamanho dos logs. Crie ou edite /etc/docker/daemon.json.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Isto limita cada container a 30 MB de logs. Todos os valores em log-opts têm de ser strings, incluindo os valores numéricos. Valide o ficheiro antes de reiniciar, porque um daemon.json malformado impede o daemon de arrancar e derruba todos os containers.
python3 -m json.tool /etc/docker/daemon.json
sudo systemctl restart docker
docker info | grep -i 'logging driver'docker info deve agora indicar Logging Driver: json-file. Os próprios limites aparecem na secção LogConfig de docker inspect num container criado depois do reinício. Este é o detalhe importante: esta definição aplica-se apenas a containers novos. Os containers existentes mantêm a configuração com que foram criados, por isso recrie-os.
docker compose up -d --force-recreateO mesmo limite pode ser definido por serviço num ficheiro Compose. Esta é a melhor opção quando um serviço ruidoso precisa de um valor próprio.
services:
app:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"Agende uma limpeza seletiva. Execute-a semanalmente, limitada a imagens dangling e à cache antiga de builds. Nunca coloque -a ou --volumes num job agendado, porque um job executado enquanto uma stack está parada elimina as imagens dessa stack e, com --volumes, começa a eliminar os seus dados.
sudo tee /etc/cron.weekly/docker-prune >/dev/null <<'EOF'
#!/bin/sh
docker image prune -f
docker builder prune -f --filter until=168h
EOF
sudo chmod +x /etc/cron.weekly/docker-prune
sudo /etc/cron.weekly/docker-pruneA última linha executa o script manualmente uma vez, para que veja o resultado antes de ele ser executado sem supervisão. O ficheiro tem de ser executável e o nome não pode conter um ponto, porque run-parts ignora tudo o que não seja executável e tudo o que tenha uma extensão.
Crie um alerta para o espaço livre. Uma limpeza executada depois de o disco ficar cheio é uma recuperação. Um alerta aos 80 por cento é prevenção.
usage=$(df --output=pcent / | tr -dc '0-9')
[ "$usage" -ge 80 ] && echo "root filesystem at ${usage} percent full"Coloque isso no cron com o notificador que já utiliza. O espaço livre é apenas metade do problema, por isso associe o alerta a monitorização da saúde do disco no seu VPS, porque um disco com falhas e um disco cheio impedem ambos os containers de arrancar, mas exigem correções diferentes.
Tudo acima pressupõe uma instalação padrão com a raiz de dados em /var/lib/docker. Se a moveu usando a chave data-root em daemon.json, substitua o caminho em todos os comandos. Definir corretamente esse layout numa máquina nova faz parte de configurar o Docker num VPS, e é muito mais fácil tomar essa decisão antes de ter 40 GB de containers na partição errada.
FAQ
O comando docker system prune elimina os meus volumes?
Não. O comando simples remove contentores parados, redes não utilizadas, imagens órfãs e a cache de build não utilizada. O pedido de confirmação lista exatamente esse conjunto. Os volumes só são abrangidos quando adiciona --volumes. Desde o Docker Engine 23.0, essa flag abrange volumes anónimos, não volumes nomeados. Os volumes nomeados são removidos por docker volume prune -a e docker compose down -v. Estes são os dois comandos que exigem cuidado.
Porque é que o disco continua cheio depois de executar docker prune?
Há duas causas habituais. A primeira são os ficheiros de log dos contentores em /var/lib/docker/containers/. Nenhum comando prune os afeta, e eles crescem sem limite até definir max-size. A segunda é um ficheiro eliminado que continua aberto por um processo. Se removeu um log com rm enquanto o respetivo contentor estava em execução, o daemon mantém o descritor de ficheiro aberto e o kernel não liberta os blocos. Por isso, df não apresenta alterações. Compare sudo du -xh --max-depth=1 /var/lib/docker com docker system df para identificar qual das situações se aplica.
Qual é a diferença entre docker image prune e docker image prune -a?
O comando simples remove apenas imagens órfãs, ou seja, imagens que perderam a etiqueta, quase sempre devido a um novo build. A forma -a remove todas as imagens que não são utilizadas por nenhum contentor existente, incluindo imagens etiquetadas que obteve deliberadamente. Depois de um docker compose down, os contentores desaparecem, por isso -a também removerá as imagens dessa stack. Nada é perdido permanentemente, porque o arranque seguinte volta a obtê-las ou a reconstruí-las. No entanto, numa ligação lenta, isso pode demorar bastante.
Como impeço que os logs do Docker encham o disco?
Defina max-size e max-file em log-opts, dentro de /etc/docker/daemon.json, e reinicie o daemon com sudo systemctl restart docker. A definição só se aplica aos contentores criados depois desse reinício. Por isso, recrie os contentores em execução com docker compose up -d --force-recreate. Pode definir as mesmas duas opções por serviço num ficheiro Compose, dentro de uma chave logging. Esta é a opção adequada quando um serviço gera muito mais logs do que os restantes.
É seguro executar docker system prune numa tarefa cron?
O comando simples docker system prune -f é seguro num host onde todas as stacks permanecem em execução. No entanto, remove contentores parados. Por isso, eliminará um contentor que parou deliberadamente e que pretendia reiniciar mais tarde. A tarefa agendada mais segura é docker image prune -f juntamente com docker builder prune -f --filter until=168h. Esta combinação liberta os dois recursos que crescem mais depressa e não pode tocar em nenhum volume. Nunca agende -a nem --volumes.