Disco cheio: encontrar e libertar espaço no servidor Linux
Quando o df indica 100% e o du não encontra nada: o caminho completo, da medição até à libertação de espaço, para Debian 12 e 13 e para Ubuntu 22.04 e 24.04.
Um disco cheio raramente avisa com delicadeza. Normalmente falha primeiro um serviço: o MariaDB escreve OS error 28 no log, o nginx responde com 500, um backup aborta com write error: No space left on device e, no pior dos casos, já ninguém consegue chegar à máquina por SSH, porque o sshd deixa de conseguir criar ficheiros de sessão. Este artigo mostra o caminho completo: medir, localizar, libertar, verificar. Incluindo os dois casos em que a maioria dos guias se fica pelo meio, ou seja, quando o df indica que está cheio e o du não encontra nada.
Medir primeiro: que partição está afinal cheia?
Antes de apagar seja o que for, é preciso saber que sistema de ficheiros está afetado. Um /boot cheio tem causas completamente diferentes de um /var cheio.
df -h
df -hT -x tmpfs -x devtmpfs -x squashfs
A segunda linha oculta os pseudossistemas de ficheiros. No Ubuntu isso é especialmente útil, porque ali cada aplicação Snap instalada aparece como um loop mount squashfs próprio e enche a saída de ruído. Já agora, esses loop mounts estão sempre ocupados a 100%, o que é normal e não representa problema nenhum.
A coluna importante é a Mounted on. Constelações típicas em servidores:
| Mountpoint | Causa habitual quando está cheio |
|---|---|
| / | Logs, Docker, cache do apt, dados de aplicações |
| /boot | kernels antigos e as respetivas imagens initramfs |
| /var | Journal, rsyslog, fila de correio, bases de dados, Docker |
| /tmp | uploads interrompidos, sessões, restos de builds |
Mais um pormenor que causa confusão com frequência: por predefinição, o ext4 reserva cinco por cento da capacidade para o utilizador root. Um serviço que corra como www-data ou mysql já recebe um No space left on device enquanto o root, no mesmo segundo, ainda escreve sem qualquer problema. Em partições de dados puras (ou seja, fora do sistema de ficheiros raiz), essa reserva pode ser reduzida sem risco:
tune2fs -m 1 /dev/sdb1
Em / a reserva deve ficar como está. É precisamente essa almofada que permite reparar um sistema que encheu por completo.
Usar o du corretamente, em vez de se perder no caminho
A abordagem clássica é percorrer um nível de cada vez, sempre com -x:
du -xh --max-depth=1 / | sort -h
du -xh --max-depth=1 /var | sort -h
O -x é a opção mais importante de todo o artigo. Mantém o du dentro de um único sistema de ficheiros e evita que o comando mergulhe em /proc, /sys, partilhas de rede montadas ou discos de backup. Sem o -x, a pesquisa demora minutos e devolve números que nada têm a ver com a partição cheia. O sort -h ordena corretamente os tamanhos legíveis por humanos, pelo que o maior bloco fica em baixo.
Quem preferir navegar de forma interativa instala o ncdu e inicia-o igualmente com -x:
apt-get install -y ncdu
ncdu -x /
Ficheiros grandes isolados encontram-se mais depressa de forma direta:
find /var -xdev -type f -size +100M -exec ls -lh {} +
Há duas armadilhas que levam regularmente a conclusões erradas. Primeira: o du conta blocos ocupados, não o tamanho lógico do ficheiro. Em ficheiros esparsos (tablespaces de bases de dados, imagens de discos virtuais), os dois valores afastam-se bastante. A comparação torna isso visível:
du -sh /var/log
du --apparent-size -sh /var/log
Segunda: o du conta os hard links apenas uma vez. Quem trabalhar sem root recebe ainda linhas como du: cannot read directory '/var/lib/private': Permission denied e, com isso, somas sistematicamente demasiado baixas. Faça portanto todas as análises como root ou com sudo.
Limitar o journal do systemd
Em servidores, o journal é o devorador silencioso de armazenamento mais frequente. Por predefinição pode ocupar dez por cento do sistema de ficheiros, com um limite máximo de quatro gigabytes, e mantém ainda 15 por cento do sistema de ficheiros livres. Num disco de 500 gigabytes, isso dá até quatro gigabytes só de log.
journalctl --disk-usage
Aqui existe uma diferença real entre distribuições que muitos guias omitem. Com a predefinição Storage=auto, o facto de o journal chegar sequer ao disco depende apenas de o diretório /var/log/journal existir:
ls -d /var/log/journal
ls -d /run/log/journal
Em instalações mínimas de Debian e em muitas imagens cloud de Debian 12 e Debian 13, o /var/log/journal não existe. Nesse caso, o journal fica em /run/log/journal, ou seja, em RAM, desaparece a cada reinício e não pesa nada no disco. Em contrapartida, pesa na memória. Já o Ubuntu Server 22.04 e 24.04 costumam criar o diretório e registam permanentemente no disco. Verifique, em vez de adivinhar.
Para libertar espaço de imediato:
journalctl --rotate
journalctl --vacuum-size=200M
journalctl --vacuum-time=7d
A chamada de --rotate antes disso não é decoração: as opções vacuum apagam exclusivamente ficheiros de journal já arquivados, nunca o que está ativo no momento. Se o ficheiro ativo representar a maior parte, sem rotação prévia parece não acontecer nada, e é exatamente aí que tropeçam os leitores que copiam o comando de um fórum.
Para limitar de forma duradoura, use um ficheiro próprio, para que atualizações de pacotes posteriores não sobrescrevam nada:
mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=200M\nRuntimeMaxUse=50M\n' > /etc/systemd/journald.conf.d/00-size.conf
systemctl restart systemd-journald
Para confirmar que surtiu mesmo efeito: o journalctl --disk-usage tem agora de indicar um valor menor e o df -h tem de mostrar mais espaço livre. Se o tamanho do journal encolher enquanto o df se mantém inalterado, há um processo que ainda mantém abertos ficheiros já apagados. Voltamos a isso mais abaixo.
Segunda diferença entre distribuições: em instalações clássicas de servidor de Debian e Ubuntu corre muitas vezes o rsyslog em paralelo, que escreve as mesmas mensagens uma segunda vez para /var/log/syslog. Em imagens mínimas de Ubuntu (cloud, container), o rsyslog não está presente. Verifique com ls -l /var/log/syslog. Se o ficheiro existir e for enorme, o problema não é o journal, mas sim uma regra de logrotate em falta ou avariada em /etc/logrotate.d/.
Cache do apt e kernels antigos
Os pacotes descarregados ficam guardados depois da instalação. Num servidor que corre há muito tempo, isso chega rapidamente a vários gigabytes.
du -sh /var/cache/apt
apt-get clean
du -sh /var/lib/apt/lists
O apt-get clean esvazia por completo o /var/cache/apt/archives, o apt-get autoclean apenas os pacotes que já não existem nas fontes. O que o clean não toca são as listas de pacotes em /var/lib/apt/lists. Com muitas fontes ativas, essas listas podem chegar a várias centenas de megabytes e podem ser reconstruídas sem risco:
rm -rf /var/lib/apt/lists/*
apt-get update
A segunda linha não é opcional, é obrigatória, e logo a seguir. Entre a eliminação das listas e o próximo apt-get update, o apt deixa de conhecer um único pacote: qualquer apt-get install nesse estado aborta com E: Unable to locate package ... e código de saída 100, embora o pacote esteja obviamente disponível nas fontes.
O segundo clássico são os kernels antigos. Através do unattended-upgrades, o Ubuntu instala continuamente kernels novos, mas só remove os antigos se isso for autorizado de forma explícita. Cada kernel ocupa, já com o initramfs, cerca de 100 a 150 megabytes num /boot que muitas vezes tem apenas 512 megabytes a um gigabyte.
uname -r
dpkg -l 'linux-image-*'
apt-get autoremove --purge
O kernel em execução indicado por uname -r nunca é removido, tal como o mais recente de cada momento. No Ubuntu previne-se o problema colocando em /etc/apt/apt.conf.d/50unattended-upgrades a linha Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";. No Debian 12 e 13, o unattended-upgrades não está ativo por predefinição, pelo que ali o /boot só cresce se alguém atualizar regularmente à mão e nunca arrumar.
Quando o /boot já está cheio e o apt deixa de correr até ao fim
Este é o caso que dói mesmo a sério. Mensagens típicas, tal como aparecem:
update-initramfs: failed for /boot/initrd.img-6.8.0-60-generic with 1.
dpkg: error processing package linux-image-6.8.0-60-generic (--configure):
installed linux-image-6.8.0-60-generic package post-installation script subprocess returned error exit status 1
E: Sub-process /usr/bin/dpkg returned an error code (1)
Agora a base de dados de pacotes está num estado meio concluído e qualquer chamada seguinte ao apt falha no mesmo ponto. A saída, por esta ordem:
- Anote o
uname -r. Nesta versão não se toca em circunstância alguma. - Execute
ls -lh /boote identifique a versão mais antiga que não esteja em execução. - Apague apenas o respetivo
initrd.img-*, não ovmlinuz-*. O ficheiro initramfs é de longe o maior e pode ser gerado de novo a qualquer momento. - Execute
apt-get -f install, para que o dpkg possa concluir a configuração interrompida. - Só depois
apt-get autoremove --purge, para que os pacotes antigos desapareçam de forma limpa, incluindo a respetiva entrada no GRUB. - Corra
update-grube leia a saída.
O que não se deve fazer: apagar ficheiros de kernel do /boot ao calhas com rm e ignorar o resto. O dpkg continua a achar que os pacotes estão instalados, o GRUB oferece entradas que apontam para o vazio e o reinício seguinte acaba na prompt de recuperação do GRUB. Quem já tiver apagado recupera a consistência com apt-get install --reinstall do pacote afetado ou com dpkg --purge, seguido obrigatoriamente de update-grub.
Verificação: o df -h /boot volta a mostrar espaço livre, o dpkg -l 'linux-image-*' lista apenas duas ou três entradas com o estado ii e a saída do update-grub nomeia exatamente os kernels que estão mesmo em /boot.
Docker, Snap e logs de containers
Em hosts Docker, a resposta está quase sempre em /var/lib/docker. Não adivinhe, pergunte:
docker system df
docker system df -v
A variante detalhada separa de forma clara imagens, containers, volumes e cache de build. Depois disso, arrume de forma direcionada:
docker image prune -a
docker builder prune
docker system prune -a
Uma palavra de cautela quanto ao --volumes: esta opção apaga também volumes aos quais não esteja ligado nenhum container em execução. Quem tenha a base de dados num volume nomeado e tenha acabado de parar o container perde assim os dados. Sem um backup atual, o docker system prune -a --volumes não tem lugar num servidor de produção.
A rubrica subestimada são os logs de containers em /var/lib/docker/containers/*/*-json.log. Sem uma indicação explícita, o driver predefinido json-file não roda absolutamente nada. Um container falador escreve assim, ao longo de meses, dezenas de gigabytes num único ficheiro. O antídoto fica em /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "3" }
}
A seguir, systemctl restart docker. Atenção, e isto quase nenhum guia refere: a definição só vale para containers recém-criados. Os existentes mantêm a configuração antiga até serem criados de novo, no caso do Compose através de docker compose up -d --force-recreate.
As versões do Docker diferem bastante neste ponto. O Debian 12 traz docker.io 20.10, o Debian 13 traz 26.1 e o Ubuntu 22.04 e 24.04 estão entretanto na 29.1. Quanto mais recente for a versão, mais espaço ocupa a cache do BuildKit e mais importante se torna o docker builder prune, porque o docker system prune sozinho nem sempre a esvazia por completo.
No Ubuntu junta-se ainda o Snap. As revisões antigas ficam desativadas mas guardadas e continuam a ocupar espaço:
snap list --all
snap set system refresh.retain=2
O valor 2 é o mínimo que o snapd aceita, valores menores são rejeitados com uma mensagem de erro. O Debian não traz Snap, pelo que ali esta secção simplesmente não se aplica.
O df diz que está cheio, o du não encontra nada
Agora o caso interessante. O df -h mostra 100%, mas a soma do du dá apenas metade. Para isso existem exatamente quatro causas plausíveis.
Ficheiros apagados mas ainda abertos
Esta é, de longe, a razão mais frequente. Alguém executou rm /var/log/riesig.log enquanto um serviço ainda mantinha o ficheiro aberto. A entrada de diretório desapareceu, por isso o du deixa de ver alguma coisa. Os blocos continuam ocupados até que o último descritor de ficheiro seja fechado, e por isso o df continua a vê-los.
apt-get install -y lsof
lsof +L1
Na coluna NLINK aparece então um 0 e, a seguir ao caminho, surge (deleted). Se o lsof não encontrar nada, não há saída nenhuma e o valor de retorno é 1, o que não é um erro. Sem o lsof também dá para o fazer diretamente através do sistema de ficheiros de processos:
ls -l /proc/*/fd 2>/dev/null | grep deleted
Importante: execute obrigatoriamente como root. Enquanto utilizador normal só vê os seus próprios descritores e escapam-lhe precisamente os serviços de sistema que, em nove de cada dez casos, são os responsáveis.
A forma limpa de libertar o espaço é reiniciar o serviço, por exemplo com systemctl restart rsyslog. Se um reinício estiver fora de questão, o ficheiro pode ser truncado para comprimento zero através do seu descritor. O ID do processo e o número do descritor vêm da saída do lsof:
truncate -s 0 /proc/1234/fd/7
Isto liberta os blocos de imediato e o processo continua a escrever a seguir. Este método destina-se a ficheiros de log puros, abertos em modo de acréscimo. Nunca o aplique a ficheiros de bases de dados, a imagens de máquinas virtuais ou a qualquer outra coisa com acesso aleatório, porque aí provoca perda de dados.
Como se reconhece que resultou: o df -h mostra logo mais espaço livre e o lsof +L1 deixa de listar a entrada. Uma segunda passagem do du, essa, não muda absolutamente nada, porque ali o ficheiro já era invisível antes. É exatamente por isso que reiniciar o servidor parece resolver o problema por magia.
Ficheiros escondidos por baixo de um mountpoint
Um clássico: alguém escreveu dados em /mnt/backup antes de o disco propriamente dito estar ali montado. Os dados continuam no sistema de ficheiros raiz, mas ficam escondidos pelo mount que está por cima. Isso só se torna visível através de uma segunda vista do mesmo sistema de ficheiros:
mkdir -p /mnt/rootview
mount --bind / /mnt/rootview
du -xh --max-depth=2 /mnt/rootview | sort -h
umount /mnt/rootview
O bind mount não tem perigo, não muda nenhuma montagem e é removido novamente com umount.
A reserva de root e o erro de permissões
Os cinco por cento de reserva do ext4 já mencionados explicam a distância entre "ainda não está totalmente cheio" e "os serviços já falham agora". E, por fim: quem inicia o du sem root não vê árvores de diretórios inteiras. A diferença aparente é então simplesmente um problema de permissões. Em btrfs e ZFS entram ainda em conta os snapshots como explicação, que o du também nunca mostra.
Quando o que acaba não é o espaço, mas os inodes
Existe um segundo tipo de "cheio" que produz exatamente a mesma mensagem de erro. Cada ficheiro e cada diretório ocupa um inode e, no ext4, o número deles está fixado desde o momento da formatação.
df -i
stat -f /
Se no df -i a coluna IUse% estiver a 100 enquanto o df -h indica bastante espaço livre, o diagnóstico é claro: o servidor não tem um problema de espaço, tem um problema de quantidade. Milhões de ficheiros minúsculos gastaram todos os inodes. Ainda assim, a mensagem de erro continua a ser No space left on device, e é precisamente por isso que a maioria procura no sítio errado.
Para encontrar os responsáveis, conte ficheiros em vez de bytes:
find /var -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head -20
Candidatos frequentes são a fila de correio em /var/spool/postfix ou /var/spool/exim4, as sessões de PHP em /var/lib/php/sessions, diretórios de cache de aplicações web, árvores de dependências do Node espalhadas pelo disco e um /tmp que nunca é esvaziado.
Na própria eliminação espera o obstáculo seguinte. Com muitíssimos ficheiros, o rm /var/lib/php/sessions/* falha com bash: /usr/bin/rm: Argument list too long, porque a linha de comandos ultrapassa o limite de tamanho. O caminho que funciona sempre:
find /var/lib/php/sessions -type f -mtime +7 -delete
O que é preciso saber: o número de inodes de um sistema de ficheiros ext4 já existente não pode ser aumentado depois. Só ajuda aumentar o sistema de ficheiros (com isso os inodes crescem proporcionalmente) ou criá-lo de novo com uma densidade maior, por exemplo com mkfs.ext4 -i 8192 /dev/sdb1. Quem já saiba de antemão que vai trabalhar com muitíssimos ficheiros pequenos, como em servidores de correio ou arquivos de imagens, fica mais bem servido com XFS, porque o XFS cria inodes dinamicamente e, na prática, só se esgota quando o espaço também se esgota. O Debian e o Ubuntu formatam por predefinição com ext4, o XFS tem de ser escolhido de forma consciente.
Verificar e garantir que não volta a acontecer
Uma ação de limpeza só conta como bem-sucedida quando três coisas batem certo: o df -h mostra mais espaço livre, o df -i mostra uma ocupação de inodes a descer e o serviço que originalmente falhou volta a correr. Um comando que terminou sem mensagem de erro não é, por si só, uma prova. Sobretudo o journalctl --vacuum-size e o docker system prune gostam de não fazer rigorosamente nada, e sem dizer uma palavra.
Para a operação contínua, dá bom resultado uma lista curta: limitar o journal de forma rígida com SystemMaxUse, definir a rotação dos logs do Docker no daemon.json, ativar no Ubuntu o Remove-Unused-Kernel-Packages, abranger os logs das suas próprias aplicações através de /etc/logrotate.d/ e criar um cronjob simples que envie um e-mail quando um limite for ultrapassado. Em servidores KVM e em servidores dedicados vale ainda a pena colocar /var, ou pelo menos /var/log, numa partição própria. Assim, um descontrolo no log paralisa um serviço, mas não o sistema operativo, e continua sempre a ser possível chegar à máquina por SSH.
Perguntas frequentes
Porque é que o df mostra 100% quando o du encontra bastante menos?
Quanto espaço pode o journal do systemd ocupar e como o limito de forma duradoura?
O journal fica no mesmo sítio em Debian e Ubuntu?
O /boot está cheio e o apt aborta com um erro do dpkg. E agora?
O "docker system prune -a --volumes" é seguro?
O que fazer quando o df -i mostra os inodes ocupados a 100%?
2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

