Existe um tipo de incidente particularmente frustrante: o disco está "cheio", mas df diz que sobra espaço. Ou o oposto — você apaga um monte de arquivo, rm roda sem erro, mas o espaço não volta. Ou ainda: uma aplicação começa a falhar ao criar arquivo novo, com uma mensagem de "no space left on device", mesmo com gigabytes livres visíveis no df -h.
Nenhum desses três cenários é bug de sistema operacional. São três armadilhas clássicas de gestão de disco em Linux, e todas elas têm explicação — e diagnóstico — através de dois comandos que parecem simples, mas que a maioria usa só na superfície: du e df.
A diferença fundamental entre du e df
Antes de qualquer exemplo, vale fixar a diferença conceitual, porque é a causa da maior parte da confusão:
df(disk free) reporta o espaço em nível de sistema de arquivos (partição/montagem) — o que o kernel considera usado e livre naquele filesystem.du(disk usage) reporta o espaço ocupado por arquivos e diretórios específicos — percorrendo a árvore de diretórios e somando o tamanho de cada arquivo encontrado.
Na maioria das vezes eles concordam. Mas existem cenários — e são exatamente os mais interessantes para troubleshooting — onde eles divergem, e entender por que divergem é o que resolve o incidente.
df: visão geral do sistema de arquivos
O uso mais básico:
df -h
A flag -h (human-readable) é praticamente obrigatória no dia a dia — sem ela, df mostra os valores em blocos de 1K, que ninguém lê de cabeça:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 42G 6G 88% /
/dev/sdb1 200G 180G 10G 95% /data
Isso já responde a pergunta mais comum — "qual partição está enchendo?" — de forma imediata. Numa investigação de disco cheio, df -h é sempre o primeiro comando a rodar.
Verificando inodes, não só espaço: df -i
Aqui está o cenário que mais pega gente de surpresa: um sistema pode ficar sem espaço para criar arquivos novos mesmo com espaço em disco disponível, se ele ficar sem inodes livres.
Todo sistema de arquivos reserva uma quantidade fixa de inodes na hora da formatação — uma estrutura que guarda metadados de cada arquivo (dono, permissão, ponteiros para os blocos de dados, entre outros). Não importa o tamanho dos arquivos: cada arquivo, por menor que seja, consome um inode. Se o número de arquivos ultrapassar o total de inodes disponíveis, novos arquivos não podem ser criados — mesmo que sobrem gigabytes de espaço em bytes.
df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 3270000 6800 99% /
Nesse exemplo, a partição está com 99% dos inodes usados — mesmo que df -h mostrasse espaço livre em GB, o sistema já está praticamente incapaz de criar arquivo novo. Esse é o cenário clássico de aplicações que geram muitos arquivos pequenos: sessões de PHP, cache de aplicação, filas de e-mail, diretórios de log rotacionados de forma agressiva, ou — bastante comum em ambientes de desenvolvimento Python — uma quantidade grande de arquivos .pyc acumulados em diretórios __pycache__ espalhados por dependências.
O sintoma típico desse problema é uma mensagem enganosa: a aplicação reporta erro de "sem espaço em disco" (ENOSPC / "No space left on device"), o time olha df -h, vê espaço livre, e fica sem entender nada — até rodar df -i e perceber que o problema nunca foi espaço, foi quantidade de arquivos.
Verificando um filesystem específico
df -h /var/log
Passar um caminho específico mostra a partição em que aquele caminho está montado — útil quando o sistema tem múltiplos discos/partições e você quer confirmar exatamente qual filesystem está sob pressão, sem precisar identificar visualmente na lista completa.
du: descobrindo o que está ocupando o espaço
Depois que df aponta qual partição está cheia, o próximo passo natural é descobrir o que, dentro dela, está consumindo o espaço. É aqui que du entra.
O básico: tamanho total de um diretório
du -sh /var/log
# 3.2G /var/log
-s resume em um único total (sem isso, du lista recursivamente cada subdiretório, o que costuma ser informação demais de uma vez). -h, de novo, para leitura humana.
O uso mais valioso para troubleshooting: ranquear subdiretórios por tamanho
Esse é, provavelmente, o comando mais útil deste artigo inteiro para investigar disco cheio:
du -h --max-depth=1 /var | sort -rh
2.1G /var/log
890M /var/lib
340M /var/cache
12M /var/spool
--max-depth=1 limita a profundidade a um nível abaixo do caminho informado — em vez de listar cada arquivo individual recursivamente (o que seria informação excessiva demais para começar a análise), ele mostra o total de cada subdiretório direto. Combinado com sort -rh (ordenar de forma reversa, respeitando sufixos como K/M/G), o resultado sai já ordenado do maior para o menor consumidor — indo direto ao ponto de onde investigar em seguida.
O fluxo natural de uma investigação de disco cheio é repetir esse comando "descendo" no diretório que aparece no topo:
du -h --max-depth=1 /var/log | sort -rh
# descobre que /var/log/app é o maior
du -h --max-depth=1 /var/log/app | sort -rh
# descobre que app.log.1, app.log.2... são os culpados
Encontrando os maiores arquivos individuais, não só diretórios
Às vezes o problema não é um diretório com muitos arquivos pequenos, mas um único arquivo enorme — um dump de banco esquecido, um core dump, um log que nunca rotacionou:
find / -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
Aqui vale explicar dois detalhes desse comando (que conecta diretamente com o artigo anterior sobre find): -xdev impede que a busca atravesse para outros sistemas de arquivo montados (evita, por exemplo, escanear um NFS montado ou um disco USB só porque está dentro da árvore de /), e 2>/dev/null descarta mensagens de "permissão negada" que normalmente aparecem aos montes ao rodar como usuário não-root sobre /.
Uma alternativa, combinando du com sort, para achar os maiores arquivos dentro de um diretório específico:
du -ah /var/log | sort -rh | head -20
-a (all) faz o du listar arquivos individuais, não só diretórios — e head -20 limita aos 20 maiores, já ordenados.
O cenário clássico: apaguei o arquivo, mas o espaço não voltou
Esse é, sem exagero, um dos incidentes mais comuns e mais mal compreendidos em servidores Linux. O cenário: um log gigante está enchendo o disco, alguém roda rm arquivo.log, o comando executa sem erro — e df -h continua mostrando o disco cheio.
A explicação está em como o Linux gerencia arquivos abertos. Quando um processo tem um arquivo aberto, apagar o arquivo (rm) remove a entrada do diretório — o "nome" pelo qual você o encontra — mas não libera o espaço em disco enquanto existir algum processo com aquele arquivo ainda aberto. O espaço só é liberado de fato quando o processo fecha o file descriptor (ou é reiniciado).
Esse cenário é frequentíssimo com logs: uma aplicação abre app.log no início e escreve nele continuamente. Se alguém apaga app.log diretamente (em vez de usar uma ferramenta de rotação de log como logrotate), o arquivo desaparece do ls, mas a aplicação continua escrevendo nele — o espaço fica "preso", invisível, sem nenhum arquivo listável para apontar o dedo.
Diagnosticando isso: lsof
lsof +L1
Esse comando lista arquivos abertos que têm link count menor que 1 — ou seja, arquivos que foram apagados (não têm mais nenhum nome no sistema de arquivos), mas continuam ocupando espaço porque algum processo ainda os mantém abertos:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
app 1842 www 4w REG 8,1 2147483648 0 1234 /var/log/app.log (deleted)
Repare no SIZE/OFF — 2 GB nesse exemplo — e no (deleted) no nome. Esse é exatamente o espaço "fantasma" que some do du (porque o arquivo não existe mais na árvore de diretórios) mas continua contando no df (porque o kernel ainda reserva o bloco de disco para ele).
A solução correta não é kill -9 no processo às cegas — é normalmente reiniciar o processo de forma controlada (o que fecha o file descriptor e libera o espaço de verdade), ou, se o processo suportar, enviar um sinal que faça ele reabrir os arquivos de log (muitos daemons tratam SIGHUP dessa forma, especialmente quando configurados junto com logrotate).
Hard links, sparse files e por que du e df às vezes discordam
Vale mencionar dois outros motivos, menos comuns, mas reais, pelos quais du e df podem mostrar números diferentes:
- Hard links: se dois nomes de arquivo apontam para o mesmo inode (mesmo conteúdo físico no disco),
dupode contar o mesmo espaço duas vezes ao somar os dois caminhos separadamente — dependendo de como a busca foi feita — enquanto o espaço real ocupado no disco é só um. - Sparse files (arquivos esparsos): alguns arquivos — comuns em imagens de disco virtual, por exemplo — têm um tamanho "lógico" grande, mas ocupam bem menos espaço físico real, porque blocos vazios não são fisicamente escritos.
dumostra o espaço físico realmente ocupado;ls -lhmostra o tamanho lógico do arquivo — e a diferença entre os dois pode ser enorme.
ls -lh arquivo.img # tamanho lógico
du -h arquivo.img # espaço físico real ocupado
Se os dois números forem muito diferentes, é sinal de que o arquivo é esparso — não é bug de nenhum dos dois comandos, é a natureza do arquivo.
Um roteiro prático de troubleshooting
Juntando tudo num fluxo de investigação típico, de cima para baixo:
# 1. Qual partição está cheia?
df -h
# 2. É espaço ou é inode?
df -i
# 3. Se for espaço: qual diretório está consumindo mais?
du -h --max-depth=1 / | sort -rh
# 4. Descer no diretório apontado, repetindo o passo 3
du -h --max-depth=1 /var/log | sort -rh
# 5. Se não encontrar um culpado óbvio na árvore de arquivos,
# verificar arquivos deletados ainda abertos por processos
lsof +L1
# 6. Se for inode: qual diretório tem mais arquivos?
find / -xdev -type d -exec sh -c 'echo "$(find "$1" -maxdepth 1 | wc -l) $1"' _ {} \; 2>/dev/null | sort -rn | head -20
O último comando merece uma explicação: para cada diretório encontrado por find, ele conta quantos itens existem diretamente dentro dele (wc -l na saída de outro find, limitado a um nível com -maxdepth 1), e ordena o resultado do maior para o menor — apontando exatamente qual diretório tem a maior quantidade de arquivos, o que é o sintoma direto de esgotamento de inodes.
Boas práticas
- Sempre confira
df -ijunto comdf -hem qualquer investigação de "sem espaço em disco" — a mensagem de erro do sistema é a mesma para os dois problemas, mas a causa e a solução são completamente diferentes. - Use
du -h --max-depth=1 | sort -rhcomo ferramenta padrão de investigação, descendo nível por nível até achar o consumidor real — evita ficar perdido numa listagem recursiva completa. - Antes de apagar um log grande diretamente com
rm, verifique se algum processo o mantém aberto (lsof arquivo) — se mantiver, prefira truncar o conteúdo (> arquivo.logoutruncate -s 0 arquivo.log) em vez de apagar, ou reinicie/sinalize o processo depois. - Configure
logrotatepara qualquer aplicação que gere log continuamente, em vez de depender de limpeza manual — evita cair recorrentemente no mesmo problema de espaço "preso" em arquivo deletado. - Monitore inodes, não só espaço em disco, em sistemas de monitoramento (Prometheus, Zabbix, CloudWatch) — é comum configurar alerta só para
%de disco usado e esquecer completamente do%de inodes usados. - Combine
ducomfind -xdevao investigar/, para não atravessar acidentalmente para outros filesystems montados (NFS, discos externos) e poluir a análise com dados de outro dispositivo.
Conclusão
df e du parecem comandos triviais — e para o uso mais superficial, são mesmo. Mas o troubleshooting sério de espaço em disco vive nas exceções: inodes esgotados sem nenhum aviso claro, espaço preso em arquivo deletado mas ainda aberto, arquivos esparsos que confundem quem só olha ls -lh. Entender essas exceções é o que separa "reiniciei o servidor e o problema sumiu, não sei por quê" de um diagnóstico real, que resolve a causa e evita que o mesmo incidente aconteça de novo no mês seguinte.