CLI AI

df говорит, что диск полон, du — что нет. Кто прав?

2026-05-20

Оба. df спрашивает у файловой системы, сколько блоков занято; du обходит дерево каталогов и складывает то, что видит. Когда эти двое расходятся, место держит что-то, у чего больше нет имени. Вот как это найти.

1. Убедиться, что расхождение есть

clai
$ clai сравни, что df показывает для / с тем, что du там реально находит→ df -h / && du -shx / 2>/dev/nullFilesystem      Size  Used Avail Use% Mounted on/dev/nvme1n1p2  915G  871G   44G  96% /348G	/

Занято 871 ГБ, найдено 348 ГБ. Вот этот разрыв и есть вся проблема. Флаг -x не даёт du уйти в другие примонтированные файловые системы и раздуть собственный ответ.

2. Проверить иноды прежде, чем гоняться за байтами

clai
$ clai проверь, не кончились ли иноды вместо места→ df -i /Filesystem        Inodes   IUsed      IFree IUse% Mounted on/dev/nvme1n1p2 124960768 3634368  121326400    3% /

Если IUse% упёрся в 100%, а Use% — нет, у вас миллионы мелких файлов и ни одного свободного инода. Это другая беда: место на диске есть, а записать новый файл файловой системе некуда.

3. Найти удалённые файлы, которые всё ещё держат открытыми

clai
$ clai покажи файлы, которые удалены, но всё ещё открыты процессом→ sudo lsof +L1COMMAND    PID USER   FD   TYPE DEVICE   SIZE/OFF NLINK  NODE NAMEpython3  23435 app     3u   REG  259,2 1000000000     0 41231 /var/log/app/debug.log (deleted)

Признак — NLINK 0: на файл больше не указывает ни одна запись каталога, поэтому du его не видит, но блоки остаются занятыми, пока не закроется последний файловый дескриптор. Обычно это лог, который кто-то удалил через rm, не перезапустив пишущий в него сервис.

4. Вернуть место

clai
$ clai перезапусти сервис, который держит этот удалённый файл→ sudo systemctl restart app.service

Перезапуск закрывает дескриптор, и ядро сразу освобождает блоки. df падает в ту же секунду — без размонтирования и без перезагрузки.

5. Если перезапустить прямо сейчас нельзя

clai
$ clai обнули удалённый файл, который процесс 23435 держит на дескрипторе 3→ sudo truncate -s 0 /proc/23435/fd/3

Запись через /proc/<pid>/fd/<n> опустошает файл, в который процесс продолжает писать, и при этом не убивает его. Дескриптор остаётся, логирование продолжается, место возвращается сразу.

6. Вторая классика: точка монтирования, под которой что-то спрятано

clai
$ clai проверь, не примонтировано ли что-то поверх каталога, в котором остались файлы→ sudo mount --bind / /mnt/root && du -shx /mnt/root/var14G	/mnt/root/var

Если в /var/data писали до того, как туда примонтировали диск, эти файлы так и лежат на корневой файловой системе — занимают место, которое сквозь монтирование не видно. Bind-монтирование корня их показывает.

Грабли

  • Запускайте du от root. От обычного пользователя он молча пропускает каталоги, которые не может прочитать, и вы будете искать фантом там, где на самом деле права доступа.
  • lsof +L1 нужны привилегии, чтобы видеть дескрипторы чужих процессов. Без sudo вы получите короткий и обманчиво чистый список.
  • Не удаляйте активный лог ещё раз. Именно это и создало ситуацию. Для живых логов используйте truncate -s 0 или настройте logrotate с copytruncate.

Похожие вопросы

Почему место не возвращается после rm? В Unix удаление убирает имя, а не данные. Блоки освобождаются, когда закроется последний открытый дескриптор, — а для работающего сервиса это значит «никогда», до перезапуска.

du и df расходятся на пару процентов — это проблема? Нет. Это зарезервированные блоки (по умолчанию 5% для root на ext4) плюс метаданные файловой системы. Разбираться стоит только с большими разрывами.

Можно найти это без lsof? Да: sudo ls -l /proc/*/fd 2>/dev/null | grep deleted показывает то же самое через таблицу процессов.

Читайте также

Не вспоминайте флаги lsof в три часа ночи — опишите проблему, а команду напишет CliAI. Установка одной строкой.