Когда в Linux внезапно начинается «linux disk full» и всё падает с no space left on device, первая реакция — посмотреть df -h и удалить пару больших файлов. Но иногда удаление «не помогает»: df по-прежнему показывает 100%, а du в каталогах не находит ничего критичного.
Чаще всего это классический случай удалённых, но открытых файлов: файл уже исчез из дерева каталогов, но процесс продолжает держать его открытым через файловый дескриптор. Ядро не освобождает занятые блоки, пока дескриптор не будет закрыт.
Симптомы: когда это точно не «просто много файлов»
Типичные признаки ситуации с удалёнными, но занятыми файлами:
df -hпоказывает 95–100% занято, запись на диск ломается.du -sh /илиduпо отдельным каталогам не объясняет, где «съедены» гигабайты.- Вы удаляете логи/дампы, а свободное место по
dfпочти не меняется. - Часто это всплывает после неудачной ротации логов (ручной
rmактивного файла, некорректные postrotate-хуки, иногда —copytruncate), либо при зависшем процессе, который продолжает писать в уже удалённый файл.
Почему df и du расходятся (du vs df простыми словами)
df смотрит на файловую систему «сверху»: сколько блоков на разделе занято (включая то, что уже не видно в каталогах). du считает «снизу»: суммирует размеры файлов, которые он может обойти по дереву директорий. Если файл удалили, он исчез из каталогов — du его уже не увидит. Но пока хотя бы один процесс держит файл открытым, блоки остаются занятыми — и это отражается в df.
Место от удалённого файла в Linux освобождается только когда файл не имеет ссылок в каталогах и его никто не держит открытым.
Быстрая проверка: вы точно смотрите на один и тот же раздел?
Частая ошибка — сравнивать разные файловые системы (bind-mount, отдельные разделы, контейнеры). Сначала определите переполненный раздел:
df -hT
Затем посмотрите «видимую» занятость по каталогам, ограничившись текущей ФС ключом -x:
du -xhd1 / | sort -h

Шаг 1. Найти открытые удалённые файлы (lsof +L1)
Самый прямой способ — lsof. Он показывает, какие процессы держат открытые файлы, включая удалённые (обычно отмечены как (deleted)).
lsof +L1
Опция +L1 выводит файлы с количеством ссылок меньше 1, то есть удалённые из каталогов, но всё ещё открытые процессами.
Если вывод большой, сузьте поиск до типичных «пожирателей» места:
lsof +L1 -nP | grep -E '/var/log|/tmp|/var/tmp'
Как быстро найти самых «толстых» виновников
У lsof обычно есть размер/смещение в колонке SIZE/OFF, но сортировать удобнее внешними инструментами. Практичный вариант для первичной диагностики:
lsof +L1 -nP | awk '{print $7, $2, $1, $9}' | sort -n | tail -n 30
Если формат «едет» из-за пробелов в путях, переходите от общего поиска к точечному: сначала определите PID по lsof +L1, затем смотрите только его (обычно виноват 1–3 процесса: веб-сервер, php-fpm, база, логгер, агент мониторинга).
Если lsof не установлен: поиск через /proc
На минимальных образах/контейнерах lsof может отсутствовать. Тогда помогут дескрипторы в /proc:
find /proc/*/fd -lname '* (deleted)' 2>/dev/null | head
Дальше вычислите PID из пути и посмотрите, что это за процесс:
ps -p 1234 -o pid,ppid,user,cmd --forest
Шаг 2. Освободить место безопасно: что делать с процессом-виновником
Вариант зависит от критичности сервиса и того, можно ли его быстро перезапустить без простоя. Главное правило: цель — закрыть дескриптор (или заставить процесс переоткрыть файл), а не «удалить ещё что-нибудь».
Вариант A (предпочтительный): корректно перезапустить сервис
Если процесс держит огромный удалённый лог, самый безопасный путь — перезапуск конкретного сервиса. После закрытия дескриптора место освободится автоматически.
systemctl restart nginx
systemctl restart php-fpm
systemctl restart postgresql
Перезапускайте точечно по данным lsof +L1, а не «всё подряд».
Вариант B: попросить процесс переоткрыть логи (без полного рестарта)
Многие демоны умеют переоткрывать лог-файлы по сигналу SIGHUP. Пример для PID:
kill -HUP 1234
Это работает только если конкретное приложение действительно обрабатывает SIGHUP как reopen logs. Для сервисов под systemd часто удобнее отправлять сигнал через systemd, но команды и поведение зависят от юнита и демона.
Вариант C (аварийный): обнулить открытый удалённый файл через /proc
Если перезапуск нельзя (например, идёт долгий экспорт), а вы уверены, что это именно лог/временный файл, можно освободить блоки, обнулив содержимое через FD.
- Найдите PID и номер FD, который указывает на
(deleted). - Запишите пустоту в дескриптор.
ls -l /proc/1234/fd | grep deleted
: > /proc/1234/fd/55
Используйте обнуление через
/procтолько для логов и временных файлов. Для данных (БД, индексы, очереди) это может привести к повреждению или неконсистентности.
Если проблема регулярно проявляется на одном и том же узле под нагрузкой, имеет смысл пересмотреть ресурсы и изоляцию: на VDS проще контролировать дисковую подсистему, лимиты и политику логирования, чем на перегруженных окружениях.
Шаг 3. Проверьте inode’ы: «место кончилось», хотя гигабайты есть
Сообщение no space left on device может появляться, даже если по гигабайтам место ещё есть. Причина — закончились inode’ы (слишком много мелких файлов).
df -ih
Если IUse% близок к 100%, найдите каталоги с лавиной мелких файлов (кеши, сессии, временные директории, spool):
find /var -xdev -type f | wc -l
find /var -xdev -type f -printf '%h
' | sort | uniq -c | sort -n | tail -n 30
Дальше включается гигиена: TTL/очистка кеша, пересмотр хранения сессий, уборка временных файлов, иногда — вынос «мелочи» на отдельный раздел.

Шаг 4. Быстрый поиск «куда ушло место», если deleted files ни при чём
Если lsof +L1 пусто или там мелочь, значит диск реально занят существующими данными. Тогда действуйте системно.
Топ по каталогам
du -xhd1 / | sort -h
du -xhd1 /var | sort -h
du -xhd1 /home | sort -h
Топ по файлам
find / -xdev -type f -printf '%s %p
' 2>/dev/null | sort -n | tail -n 30
Проверить журнал systemd и подчистить по политике
journalctl --disk-usage
Аккуратно задайте удержание, не «в ноль», если логи важны для расследований:
journalctl --vacuum-time=14d
Если это Docker/контейнеры
Частый источник внезапного заполнения — слои образов и логи контейнеров.
docker system df
docker ps --size
Про контейнерную изоляцию и варианты усиления безопасности окружения можно почитать отдельно: изоляция контейнеров: gVisor и Firecracker.
Профилактика: как не ловить disk full из-за логов и дескрипторов
Почти всегда root cause один из двух: неправильная ротация логов или отсутствие мониторинга порогов.
Ротация логов: не удаляйте активный файл «вручную»
Типичная паническая команда (так делать не стоит):
rm -f /var/log/nginx/access.log
Если nginx продолжит писать в старый дескриптор, вы получите удалённый, но растущий файл — и тот самый конфликт du vs df. Правильнее:
- настроить
logrotateпод конкретный демон; - использовать postrotate-хук, который заставляет сервис переоткрыть логи;
- избегать
copytruncateтам, где есть штатный reopen (иначе риски потери/дубликатов строк и лишняя нагрузка на I/O).
Мониторинг: отслеживайте не только % диска
Что имеет смысл алертить:
- занятость разделов по
df(например, >80% warning, >90% critical); - inode’ы по
df -i; - рост
/var/log,/var/lib/docker,/tmp; - наличие крупных
(deleted)файлов (периодическая проверкаlsof +L1по регламенту или автоматизация в мониторинге).
Короткий план действий при no space left on device
df -hT— понять, какой раздел переполнен.df -ih— исключить проблему inode’ов.du -xhd1 /— оценить «видимое» место.lsof +L1— найти удалённые, но открытые файлы.- Освободить место: перезапуск сервиса или reopen logs; аварийно — обнулить через
/proc/<pid>/fd/<fd>. - После стабилизации — поправить ротацию и мониторинг, чтобы проблема не повторилась.
Если сделать этот сценарий привычкой, «disk full в самый неподходящий момент» перестаёт быть загадкой: вы быстро отличаете реальную заполненность от «призрачных» гигабайт и аккуратно закрываете вопрос через lsof и работу с дескрипторами.


