Системный администратор получает тикет: «Диск заполнен, но df -h показывает 40% свободного места». Классическая ловушка для тех, кто не знает об inode. Сервер отказывается создавать новые файлы не из-за нехватки места — из-за нехватки индексных узлов. Именно здесь начинается настоящее знакомство с тем, как Linux хранит данные на самом деле.
Два слоя хранения данных
Большинство пользователей думают, что файл — это просто имя и содержимое. На самом деле каждый файл существует в двух независимых местах.
Первое — блоки данных: физические секторы диска, где лежит содержимое. Второе — inode (Index Node): отдельная структура фиксированного размера, где хранятся все метаданные об этом содержимом. А имя файла — это третья, совершенно отдельная вещь. Оно хранится в записи директории и просто указывает на номер inode.
Удалите запись в директории — inode никуда не денется, пока на него есть хотя бы одна ссылка. Это фундамент того, как работают жёсткие ссылки и почему открытый процессом файл можно восстановить даже после rm.

Система косвенных указателей — не просто архитектурная деталь. Именно благодаря ей ext4 адресует файлы размером до 16 ТБ, сохраняя inode фиксированного размера в 256 байт.
Что конкретно хранит inode
В отличие от распространённого заблуждения — имени файла в inode нет. Есть всё остальное:
размер файла в байтах, идентификатор устройства хранения, UID и GID владельца, маска прав доступа (mode), флаги файла, три временны́е метки (atime, mtime, ctime), счётчик жёстких ссылок и указатели на блоки данных. Блоки адресуются иерархически: прямые указатели для маленьких файлов, косвенные для средних, двойные косвенные для больших.
Три временны́е метки: в чём разница
Путаница между atime, mtime и ctime — источник неочевидных ошибок в скриптах резервного копирования и мониторинга.
mtime меняется при изменении содержимого файла. ctime — при изменении содержимого или метаданных inode (смена прав, владельца, жёстких ссылок). atime — при каждом чтении файла. Именно из-за atime на нагруженных серверах с тысячами обращений к одним и тем же файлам возникают лишние операции записи. Решение — опция монтирования noatime в /etc/fstab:
/dev/sda1 / ext4 defaults,noatime 0 1
На VPS с SSD-хранилищем это снижает износ диска и количество I/O-операций без потери функциональности.
Узнать номер inode и метаданные файла
Быстрый способ — флаг -i команды ls:
ls -i /etc/nginx/nginx.conf
Вывод будет примерно таким:
786435 /etc/nginx/nginx.conf
Для развёрнутой картины — команда stat:
stat /etc/nginx/nginx.conf
File: /etc/nginx/nginx.conf
Size: 2893 Blocks: 8 IO Block: 4096 regular file
Device: 802h/2050d Inode: 786435 Links: 1
Access: 2026-02-28 10:15:00
Modify: 2026-02-01 14:22:10
Change: 2026-02-01 14:22:10
stat показывает все три метки сразу — удобно для отладки скриптов, которые ориентируются на время изменения.
Inode заканчиваются: диагностика и поиск причины
Каждый раздел создаётся с фиксированным лимитом inode. Этот лимит задаётся при форматировании и потом не меняется (для ext4 без специальных манипуляций). На VPS и выделенных серверах исчерпание inode — не экзотика. Пара сотен тысяч временных файлов PHP-сессий или очередь Postfix из миллиона писем — и система перестаёт принимать новые файлы.
Посмотреть заполненность inode по всем разделам:
df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 1310720 128000 1182720 10% /
/dev/vda2 655360 655360 0 100% /var/spool
tmpfs 512000 1 511999 1% /run
Раздел /var/spool на 100% — почтовый сервер встал. Свободного места на диске может быть хоть 50 ГБ.
Найти виновника — директорию с аномально большим числом файлов:
find /var -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head -15
Команда выводит директории отсортированные по количеству файлов в них. Типичная картина: /var/spool/postfix/deferred с сотнями тысяч вложенных файлов, /tmp/sessions от PHP, /var/cache от пакетных менеджеров.
Найти файл по номеру inode
Сценарий встречается реже, чем хотелось бы думать: файл есть на диске, но имя неизвестно или содержит символы, которые нельзя набрать с клавиатуры. Например, файл создан через ошибку в скрипте и имя начинается с дефиса или содержит пробелы с управляющими символами.
find /home -inum 786435
Ключ -inum принимает номер inode. Ограничение поиска конкретным разделом (/home вместо /) ускоряет работу в несколько раз.
Удалить такой файл безопаснее тоже через inode:
find /home -inum 786435 -delete
Жёсткие ссылки и счётчик ссылок
Жёсткая ссылка — не копия файла и не «ссылка» в привычном смысле. Это второе имя, указывающее на тот же inode. Данные на диске единственные, счётчик ссылок увеличивается:
ln /var/log/app.log /backup/app_hardlink.log
stat /var/log/app.log | grep Links
# Links: 2
Файл исчезнет с диска только когда счётчик опустится до нуля. Поэтому rm технически называется unlink — он не удаляет данные, а отсоединяет имя от inode. Если на inode больше нет ссылок и ни один процесс не держит файл открытым — ядро освобождает блоки.
Отсюда практическое следствие: если удалить файл лога пока в него пишет nginx, файл «исчезнет» из директории, но место на диске не освободится до перезапуска nginx (или отправки сигнала USR1).
Восстановить удалённый файл через /proc
Пока процесс держит файл открытым — данные живы в /proc/PID/fd/:
# Найти дескриптор удалённого файла
lsof | grep deleted | grep nginx
# Скопировать через proc
cp /proc/12345/fd/7 /var/log/app_recovered.log
Это работает только пока процесс не завершился. После — только fsck или специализированные инструменты восстановления типа extundelete.
Шпаргалка
| Задача | Команда |
|---|---|
| Номер inode файла | ls -i filename |
| Все метаданные inode | stat filename |
| Использование inode по разделам | df -i |
| Найти файл по номеру inode | find /path -inum НОМЕР |
| Удалить файл по inode | find /path -inum НОМЕР -delete |
| Директории с наибольшим числом файлов | find /path -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head |
| Создать жёсткую ссылку | ln source linkname |
| Счётчик жёстких ссылок | stat filename | grep Links |
| Восстановить удалённый открытый файл | cp /proc/PID/fd/FD /path/recovered |