Короткий ответ
Одна и та же строка No space left on device бывает и при нуле гигабайт, и при нуле inode. Различие одно: df -h против df -i. Если IUse% = 100%, а Use% = 20%, удаление одного большого ISO не поможет — нужны миллионы мелких файлов. На Ubuntu это часто /var/lib/php/sessions, spool почты, tree lost+found после неудачного rsync, кэш nginx microcache, каталоги CI.
Не форматируйте том «чтобы inode стало больше». Сначала найдите каталог с аномальным find … | wc -l.
Симптомы и как отличить
touch /tmp/x→No space left on device;df -h /показывает десятки гигабайт свободно;df -i /—IUse% 100%;- GUI/мониторинг «диск в порядке», приложения мёртвые.
| Проверка | Гигабайты | Inode |
|---|---|---|
df -h | 100% | низкий % |
df -i | низкий % | 100% |
du -xh большого файла | да | обычно нет |
find | wc -l огромный | не обязательно | да |
Похожие, но другие проблемы: закончился диск, read-only (там Read-only file system, не ENOSPC).
Возможные причины
- PHP/Python сессии без gc: файлы по минуте в
/var/lib/php/sessions. - Почтовый spool
/var/mail,/var/spool/postfix/deferredпри сломанном relay. cronкаждую минуту создаёт уникальный лог в/var/logбез ротации — см. cron.npm/pipкэши,~/.cacheпользователя сервиса.- Массовый
touchбэкап-скриптом «маркеров». - ext4 создавался с байтами на inode по умолчанию на крошечных файлах (архив документов).
Диагностика
На host.example:
df -h
df -i
tune2fs -l /dev/vda2 | grep -E 'Inode count|Free inodes|Inode size|Block count'tune2fs требует размонтирования? Нет, -l только читает суперблок.
Поиск каталогов-лидеров по числу файлов, не по размеру:
sudo du --inodes -xd1 / | sort -n | tail
sudo du --inodes -xd1 /var | sort -n | tail
sudo du --inodes -xd1 /var/lib | sort -n | taildu --inodes есть в GNU du на Ubuntu 22.04/24.04. Если нет — медленный вариант:
sudo find /var -xdev -printf '%h\n' | sort | uniq -c | sort -n | tailПримеры точечных счётчиков:
sudo find /var/lib/php/sessions -xdev -type f | wc -l
sudo find /var/spool -xdev -type f | wc -l
sudo find /tmp -xdev -type f | wc -lНе запускайте find / без -xdev на хосте с NFS — уйдёте в сетевую ФС.
Решение
Сценарий A. Сессии PHP
Подтвердите, что это сессии, а не пользовательские данные:
ls /var/lib/php/sessions | head
sudo find /var/lib/php/sessions -type f -mtime +2 | wc -lУдаляйте только старые:
sudo find /var/lib/php/sessions -type f -mtime +2 -deleteВключите session.gc_maxlifetime и cron gc, как задумано пакетом php-common.
Сценарий B. Почтовый deferred
sudo find /var/spool/postfix -type f | wc -l
sudo postqueue -p | tailРазбор очереди — отдельно. Массовое postsuper -d ALL уничтожит легитимную почту. Сначала причина bounce.
Сценарий C. Кэши и CI
Каталоги вроде /var/cache/nginx, /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs считайте du --inodes. Чистка overlay — теми же правилами, что для диска: не rm -rf на живых snapshot.
Сценарий D. Нужно больше inode навсегда
На пустом томе пересоздание ФС с mkfs.ext4 -i 4096 (байт на inode) увеличивает число inode. На живом корне это переустановка. Альтернатива: перенести каталог-потребитель на новый том с мелким -i и bind-mount.
Почему ENOSPC путают с гигабайтами на практике
Ядро возвращает одно и то же ENOSPC (код 28) и для блоков, и для inode. Приложения почти никогда не пишут «inodes exhausted». Поэтому мониторинг только disk used % слеп. На Zabbix/Prometheus нужен node_filesystem_files_free / df -i, иначе PHP начнёт отдавать 500 при «зелёном» диске.
ext4 заранее выделяет таблицу inode при mkfs. Параметр -i bytes-per-inode по умолчанию часто 16384. На томе 20 ГБ это порядка миллиона inode. Каталог сессий PHP легко съедает их за недели, занимая сотни мегабайт. du -sh врёт «каталог маленький», du --inodes показывает правду.
На Ubuntu 24.04 systemd-tmpfiles чистит /tmp по возрасту, но не /var/lib/php/sessions, если gc PHP выключен в пуле. Проверьте:
php -i | grep -E 'session.save_path|session.gc'
ls /etc/php/*/fpm/php.iniДля mail spool смотрите не только postfix: nullmailer и exim4 кладут очередь в другие пути. find /var/spool -xdev -type f | wc -l покрывает оба.
Если виновник — git-репозиторий с миллионом blob в objects после ошибочного git add каталога node_modules, не git gc на проде без backup: сначала du --inodes по .git.
Как проверить, что проблема устранена
df -i /
touch /tmp/.inode-ok && rm /tmp/.inode-ok
sudo -u admin touch /home/admin/.inode-ok && rm /home/admin/.inode-okПовторите создание файла от пользователя приложения (www-data и т.д.). apt-get check не должен падать с ENOSPC.
Если не помогло
- IUse% падает и сразу растёт: процесс-генератор жив.
auditctl/inotifywaitна каталог, не reboot как лечение. df -i100% сразу после fsck: смотритеlost+found— ext4 после сбоя.- XFS: inode выделяются иначе; смотрите
xfs_infoиxfs_db. На Ubuntu server чаще ext4 для корня.
Профилактика
- Алерт
df -i> 70%. - Отдельный том под сессии/кэш.
- Не хранить миллионы файлов на корневом LV «пока влезает по гигабайтам».
- logrotate и tmpfiles.d для
/tmpи/var/tmp.
FAQ
Можно ли «добавить inode» без mkfs?
На ext4 нет живого resize inode count. Только новый том или чистка.
Почему мониторинг диска молчал?
Он смотрел %bytes, не %inodes. Два датчика.
rm -rf каталога идёт часами
Миллионы unlink. rsync -a --delete empty/ в пустой каталог иногда быстрее; всё равно это удаление данных — сначала убедитесь, что каталог расходный.
Связано ли это с reserved blocks?
Нет. Reserved blocks — это гигабайты для root, не inode table.
Docker overlay ест inode или байты?
Оба. Слои с кучей мелких файлов бьют именно inode. docker system df не заменяет df -i на /var/lib/docker.