Disco cheio às 3h da manhã: achar o culpado e evitar a recaída
O alerta chega de madrugada, você apaga um log, o serviço volta e duas semanas depois acontece de novo. Este é o caminho para achar o que realmente ocupa o disco — e para o problema não voltar.
Equipe EasyOps Cloud · · 8 min de leitura
No space left on device. O banco parou de aceitar escrita, o nginx devolve 500 e
o deploy falhou no meio. Você entra na máquina, apaga o log maior que encontra,
o serviço volta, e todo mundo segue a vida.
Duas semanas depois, acontece de novo — no mesmo dia da semana, no mesmo horário. Apagar arquivo é a parte fácil e é também o motivo do problema voltar. O que resolve de verdade é descobrir o que cresce, por quê, e colocar um limite que não dependa de alguém estar acordado.
Comece pelo mapa, não pelo maior arquivo
O primeiro comando não é du. É entender qual sistema de arquivos encheu, porque /
cheio e /var cheio são problemas diferentes:
df -h # espaço por ponto de montagem
df -i # inodes — a outra forma de encherA segunda linha é a que quase todo mundo pula. É perfeitamente possível ter 40%
de espaço livre e mesmo assim receber No space left on device: o disco tem bytes
sobrando, mas acabaram os inodes, que são as entradas que identificam cada
arquivo. Um diretório de sessões PHP, uma fila de e-mail ou um cache com milhões
de arquivos minúsculos esgota inode muito antes de esgotar espaço.
Se df -i mostra IUse% 100, pare de procurar arquivo grande e vá procurar
diretório com arquivo demais:
sudo find /var -xdev -type d -printf '%h\n' 2>/dev/null \
| sort | uniq -c | sort -rn | head -20Descer no diretório certo
Com o ponto de montagem identificado, desça por camada em vez de varrer o disco inteiro:
sudo du -h --max-depth=1 /var 2>/dev/null | sort -rh | head -15Repita trocando /var pelo maior resultado, até chegar ao culpado. É mais rápido
que du -sh /* e não castiga o disco de uma máquina que já está sofrendo.
O -xdev também importa: sem ele, o du atravessa pontos de montagem e você acaba
somando um bucket montado via s3fs ou um volume de rede na conta do disco local.
Os suspeitos de sempre, em ordem de frequência:
/var/log— log sem rotação, ou rotação configurada e nunca aplicada./var/lib/docker— imagem antiga, container parado, volume órfão, cache de build. Costuma ser o maior de todos em servidor de aplicação./var/lib/mysqlou/var/lib/postgresql— dado real, mas também WAL acumulado e binlog que ninguém configurou para expirar./tmp— arquivo temporário que o processo nunca apagou porque morreu antes./home— dump de banco que alguém gerou "só para testar" há oito meses.
Quando o df e o du discordam
Este é o caso que mais consome tempo de quem não conhece. O du soma 4 GB, o df
insiste que 40 GB estão ocupados, e a diferença não aparece em lugar nenhum.
A explicação é que apagar um arquivo no Linux remove apenas o nome. Se algum
processo ainda tem aquele arquivo aberto, os blocos continuam alocados até o
processo fechar o descritor ou morrer. O du percorre nomes e não vê nada; o df
pergunta ao sistema de arquivos e vê tudo.
É exatamente o que acontece quando alguém "resolve" o disco cheio com rm
access.log sem reiniciar o nginx. O espaço não volta.
# Arquivos apagados que ainda seguram espaço
sudo lsof +L1 | awk '$5=="REG" {print $1, $2, $7, $NF}' | sort -k3 -rn | headA saída traz processo, PID e tamanho. A correção é recarregar o serviço dono do arquivo, não matar nada às cegas:
sudo systemctl reload nginx # reabre os arquivos de log
sudo systemctl restart meuapp # quando reload não bastaE a lição para a próxima vez: nunca apague log ativo. Trunque, que libera o espaço sem invalidar o descritor aberto:
sudo truncate -s 0 /var/log/nginx/access.logDocker: o maior consumidor silencioso
Em servidor de aplicação, /var/lib/docker é o campeão. O Docker guarda camada de
imagem, container parado, volume que ninguém removeu e cache de build — nenhum
deles some sozinho.
docker system df # visão geral do que ocupa
docker system df -v # detalhado, por imagem e volumeA limpeza tem dois níveis, e a diferença entre eles é séria:
# Seguro: remove container parado, rede sem uso,
# imagem pendente e cache de build
docker system prune
# Agressivo: remove TAMBÉM imagem sem container rodando
docker system prune -a
# Perigoso: apaga volume — é onde vive o dado do banco
docker system prune --volumesO --volumes merece cuidado real. Se o seu Postgres em container guarda o dado em
volume nomeado e ele não está de pé no momento do prune, você acabou de apagar o
banco. Antes de rodar qualquer variação com --volumes, tire um snapshot — é a
diferença entre um susto e uma restauração de backup.
Para o cache de build, que cresce sem parar em servidor de CI:
docker builder prune --filter 'until=168h'journald e logrotate: onde colocar o limite
O journald tem limite próprio e por padrão ele é generoso demais — 10% do disco.
Em uma VPS de 80 GB isso significa 8 GB de log:
journalctl --disk-usage
sudo journalctl --vacuum-size=500M # corte imediato
sudo journalctl --vacuum-time=14d # ou por idadeO corte imediato resolve a noite. O que evita a recaída é fixar o teto em /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=500M
MaxRetentionSec=14daysudo systemctl restart systemd-journaldPara os logs que não passam pelo journal — nginx, aplicação, script de cron —
quem manda é o logrotate. Vale conferir se ele está realmente rodando, porque um
arquivo de configuração com erro de sintaxe faz o logrotate abortar todas as
rotações seguintes, não apenas a defeituosa:
sudo logrotate -d /etc/logrotate.conf # simula, não altera nada
systemctl status logrotate.timerUm arquivo mínimo e correto para a sua aplicação:
/var/log/meuapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}O copytruncate existe justamente para o caso do descritor aberto: ele copia e
trunca em vez de renomear, evitando o problema da seção anterior quando a
aplicação não sabe reabrir o próprio log.
O que dá para apagar às pressas — e o que não
Na urgência, a tentação é apagar o que for grande. Alguns alvos são seguros e outros custam caro depois.
Seguro, e o primeiro lugar para onde ir:
- Cache de pacote.
sudo apt cleanlibera/var/cache/apt/archivessem consequência nenhuma — são arquivos.debjá instalados. - Log rotacionado antigo. Os
.gzem/var/logsão histórico. Apagar os mais velhos que o seu período de retenção é exatamente o que a rotação faria. - Kernel antigo. Em Debian e Ubuntu,
sudo apt autoremove --purgecostuma liberar centenas de MB em/boot, que é uma partição pequena e um motivo comum de alarme.
Cuidado redobrado, porque parece lixo e não é:
- `/var/lib/docker/volumes`. Só olhando o nome não dá para distinguir volume órfão de volume com o banco de produção dentro.
- Arquivo grande em `/var/lib/mysql` ou `/var/lib/postgresql`. Nunca apague nada ali por fora. Binlog e WAL têm comandos próprios de expiração; remover o arquivo na mão corrompe o banco.
- Qualquer coisa em `/proc` ou `/sys`. Não são arquivos reais e não ocupam disco.
A regra que evita arrependimento: em emergência, libere o mínimo necessário para o serviço voltar — truncar um log costuma bastar — e só então investigue com calma. A decisão tomada às 3h com o serviço fora do ar é sempre pior que a mesma decisão tomada às 9h com tudo funcionando.
Pare de descobrir pelo alerta de disco cheio
Diagnosticar às 3h é o sintoma de não estar olhando antes. Duas medidas simples mudam isso.
A primeira é enxergar a tendência. Disco raramente enche de repente — ele enche em linha reta durante semanas. O painel mostra a curva de uso e é ali que dá para prever a data em que o limite chega, com folga para agir em horário comercial.
A segunda é um aviso local, útil quando o crescimento é súbito:
#!/usr/bin/env bash
# /usr/local/bin/check-disco.sh
LIMITE=85
df -P -x tmpfs -x devtmpfs | awk -v l="$LIMITE" 'NR>1 {
gsub(/%/,"",$5); if ($5+0 >= l)
printf "ALERTA: %s em %s%% (%s livre)\n", $6, $5, $4
}'Um systemd timer de hora em hora chamando esse script avisa enquanto ainda dá
para agir com calma. Se preferir cron, confira antes por que o cron às vezes não
roda — um alerta de disco que nunca dispara é pior que nenhum, porque gera
confiança falsa.
O que fazer com o que precisa ser guardado
Boa parte do que enche disco não é lixo: é dump de banco, arquivo de cliente, export mensal. Esse material não deveria estar no disco da VM, que é o armazenamento mais caro e mais frágil que você tem — some junto com a máquina.
O destino natural é um bucket. Um dump que nasce e já vai embora para o bucket S3 sai da conta do disco no mesmo dia, com custo por GB mais baixo e retenção configurável. O script de dump e envio já sai com retenção, então o crescimento fica limitado por desenho.
Se depois de limpar o disco continuar cheio sem explicação, vale seguir o diagnóstico de VPS lenta: disco cheio e disco lento produzem sintomas parecidos na aplicação, mas as causas — e as correções — não têm nada em comum.