Механика: почему "место на диске" - это две разные вещи
Файловая система хранит две вещи отдельно: сами данные (блоки на диске) и метаданные о файлах (иноды). Инод (inode) - это карточка файла: кому принадлежит, права, временные метки, размер и где лежат блоки с данными. Имя файла в иноде НЕ хранится - имя живёт в каталоге и просто ссылается на номер инода. Поэтому у одного инода может быть несколько имён (жёсткие ссылки), и это пригодится дальше.
Ключевой вывод для новичка: иноды кончаются отдельно от места. В ext4 число инодов фиксируется на этапе создания ФС (mkfs, параметр bytes-per-inode) и онлайн не меняется. Если у тебя миллионы крошечных файлов - сессии PHP, мелкие кэши, очереди писем - ты упрёшься в лимит инодов раньше, чем кончатся гигабайты. Тогда df -h покажет "свободно 40G", а система честно ответит "No space left on device". Это первая ловушка.
Важная актуализация на 2026: на новых серверах под Astra Linux, RED OS, RHEL-семейством и многих образах в облаках по умолчанию всё чаще XFS, а не ext4. У XFS иноды выделяются динамически, поэтому колонка "всего инодов" в df -i не фиксирована и плавает от запуска к запуску - пугаться этого не надо, у XFS нет того жёсткого потолка инодов, что у ext4 (потолок есть, но он процент от размера ФС и очень большой). На Btrfs картина ещё интереснее: там df вообще условен из-за сжатия и снапшотов, отдельно про это ниже. Понимать, какая под тобой ФС, важно: команда mount или findmnt покажет тип.
Код: Выделить всё
findmnt -t ext4,xfs,btrfs # какие ФС и где смонтированы
df -h # сколько занято места (блоков данных)
df -i # сколько занято инодов (карточек файлов)

df -h и df -i: читаем по колонкам
Начинаем всегда с df. Связка df du работает так: df говорит ГДЕ проблема (на какой точке монтирования), du говорит В ЧЁМ она (какой каталог раздулся).
Код: Выделить всё
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 49G 47G 1.8G 97% /
/dev/sda2 197G 45G 143G 24% /var
tmpfs 3.9G 1.2M 3.9G 1% /run
overlay 49G 47G 1.8G 97% /var/lib/docker/overlay2/...
- Filesystem - устройство или раздел. tmpfs и overlay - это не диск: tmpfs живёт в RAM, overlay - слои контейнеров Docker, которые на самом деле лежат на /. Их отдельные строки часто путают новичков.
- Size / Used / Avail - всего / занято / доступно.
- Use% - процент занятого. Сюда смотри в первую очередь. 97% на / (корне) - это тревога. Заметь: Used + Avail обычно МЕНЬШЕ, чем Size. Для ext4 причина - резерв около 5% места под root (чтобы система не встала колом и демоны root могли дописать при переполнении). df честно вычитает этот резерв из Avail, поэтому "0 доступно" по df ещё не значит, что физически 0 байт - root туда впишется. Резерв на больших дисках под данные часто снижают через tune2fs -m 1.
- Mounted on - точка монтирования. Запомни её: дальше копать du будешь именно там, где Use% высокий.
Код: Выделить всё
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 3276792 8 100% /
/dev/sda2 13107200 41210 13065990 1% /var
Практика: пошаговый поиск, куда делось место на диске
Допустим, df указал на корень / с Use% 97%. Спускаемся по дереву с помощью du. Главная команда du в Linux - с ограничением глубины:
Код: Выделить всё
$ sudo du -h --max-depth=1 -x / 2>/dev/null | sort -rh | head -15
27G /var
12G /usr
4.1G /home
2.3G /opt
49G /
Код: Выделить всё
$ sudo du -h --max-depth=1 -x /var | sort -rh | head
24G /var/log
2.1G /var/lib
$ sudo du -h --max-depth=1 -x /var/log | sort -rh | head
22G /var/log/journal
Для интерактивного режима классика - ncdu: он один раз сканирует каталог и даёт ходить по дереву стрелками, Enter заходит внутрь, d удаляет выделенное:
Код: Выделить всё
sudo ncdu -x /
Отдельно про большие файлы - иногда диск съел не каталог, а один монстр (дамп базы, незаротированный лог, core dump):
Код: Выделить всё
$ sudo find / -xdev -type f -size +1G -printf '%s %p\n' 2>/dev/null | sort -nr | head
6442450944 /var/lib/mysql/ib_logfile_old
3221225472 /home/deploy/dump.sql
Грабли: df и du расходятся, а место "не вернулось"
Грабля 1. du и df показывают разные числа. Это нормально, и у этого несколько частых причин.
Первая и самая частая - удалённый, но открытый файл. du ходит по каталогам и видит имена. Если файл удалён (имени нет), но процесс держит его открытым, du его НЕ посчитает, а df посчитает занятое место честно. Отсюда "df говорит 90%, а du насчитал 40%". Ищем держателей так:
Код: Выделить всё
$ sudo lsof +L1
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 812 root 3w REG 8,1 21474836480 0 131 /var/log/nginx/access.log (deleted)
Код: Выделить всё
sudo truncate -s 0 /proc/812/fd/3
Вторая причина расхождения - скрытое потребление под точкой монтирования. Если в каталог, скажем /mnt/data, что-то писали ДО того, как туда смонтировали диск, эти файлы никуда не делись - они лежат на родительской ФС, спрятанные под точкой монтирования. df их не видит (для него под /mnt/data уже другая ФС), а место на корне тает. Проверка: временно сделать umount и посмотреть, что под точкой, либо сравнить du -x по корню (без пересечения границ ФС) с тем, что показывает df по /.
Третья причина характерна для XFS и ext4: файлы с дырами (sparse) и спекулятивная преаллокация. XFS заранее резервирует место под растущий файл, поэтому df может на время показать больше занятого, чем du. Обычно это саморассасывается, специально лечить не нужно.
Грабля 2. Чистишь большие файлы, а IUse% всё равно 100%. Ты борешься не с тем. При нехватке инодов нужен не объём, а количество. Ищем каталог с диким числом мелких файлов. Надёжный однострочник:
Код: Выделить всё
$ sudo find / -xdev -type d 2>/dev/null -exec sh -c 'echo "$(ls -A "$1" 2>/dev/null | wc -l) $1"' _ {} \; | sort -rn | head
482310 /var/lib/php/sessions
98214 /home/app/cache/twig
Грабля 3. Забывают про квоты. Если место по df есть, инодов хватает, а пользователь жалуется "не могу записать" - может быть включена дисковая квота. Смотри quota -u имя_пользователя или repquota -a (нужен пакет quota и опция usrquota/grpquota в /etc/fstab, на XFS квоты включаются опциями монтирования и смотрятся через xfs_quota). Квота - не баг, а настроенный лимит, но в диагностике про неё забывают.
Грабля 4. Контейнеры и Btrfs. На хосте с Docker место часто съедает /var/lib/docker (старые образы, тома, логи контейнеров). df по / растёт, du по нему долгий - быстрее спросить docker system df. На Btrfs обычный df врёт из-за сжатия и метаданных - там правильнее btrfs filesystem usage /.
Мини-лаба: повтори руками прямо сейчас
Безопасно на любой тестовой машине или в контейнере:
- Выполни findmnt -t ext4,xfs - узнай тип своей ФS. Затем df -h и df -i, найди раздел с самым высоким Use% и IUse%. Сравни запас места и инодов - где меньше.
- Прогони sudo du -h --max-depth=1 -x / 2>/dev/null | sort -rh | head и за 2-3 спуска дойди до самого тяжёлого каталога. Запиши путь.
- Поставь ncdu (и, если есть, gdu) и пройди тот же путь интерактивно - почувствуй разницу в удобстве и скорости.
- Смоделируй удалённый открытый файл: в первом терминале head -c 200M /dev/zero > /tmp/test.bin, потом tail -f /tmp/test.bin. Во втором: rm /tmp/test.bin, затем sudo lsof +L1 | grep test.bin - увидишь NLINK 0 и (deleted). Сравни df до и после rm: место не вернулось. Закрой tail (Ctrl+C) - вернётся.
- df -h показывает 30% занятого, но система пишет "No space left on device". Какую команду запустишь следующей и что будешь искать в её выводе? Почему на XFS этот сценарий маловероятен?
- du насчитал 40 ГБ, а df говорит, что занято 90 ГБ. Назови две причины расхождения и команду для проверки самой частой из них.
- Что означает NLINK = 0 и пометка (deleted) в выводе lsof +L1, и как освободить место, не теряя работающий процесс?
- Почему при нехватке инодов на ext4 бесполезно удалять большие файлы, что искать вместо этого и почему на XFS этой проблемы почти нет?
Место на диске - это блоки и иноды, они кончаются независимо. Сначала df -h (где проблема) и df -i (не в инодах ли дело), потом du -h --max-depth=1 -x со спуском вниз, либо ncdu/gdu для интерактива, либо duf для красивой сводки. Если du и df расходятся - первым делом lsof +L1 (удалённый, но открытый файл) и проверка скрытого потребления под точкой монтирования. Активные логи не удаляй через rm, обнуляй через truncate -s 0; раздутый журнал systemd чисти journalctl --vacuum-size/--vacuum-time. Знай тип своей ФС: на XFS иноды динамические, на Btrfs и в Docker у места своя бухгалтерия. Эта связка закрывает 95% случаев "закончилось место в Linux".