Диск кончился: найти и почистить
No space left on device на ноде или сервере с панелью. Как за пять минут найти, что съело место, освободить его и не наступить на те же грабли через месяц.
Диск кончается всегда в самый неудобный момент: Postgres перестаёт писать, бот падает, Docker не может создать контейнер. На VPS за 3 евро с диском на 10 ГБ этот момент наступает быстрее, чем кажется.
Шаг 1. Место или иноды
df -hT
df -idf -h показывает занятые блоки, df -i иноды. Если IUse% в df -i на 100%, а место ещё есть, проблема не в размере, а в количестве файлов: миллионы мелких файлов сессий, кэша, писем. Искать надо не тяжёлые папки, а многолюдные:
du --inodes -x -d 3 / 2>/dev/null | sort -n | tail -20Заметка
df может показывать 100% ещё до того, как места нет совсем. На ext4 по умолчанию 5% блоков зарезервировано за root, чтобы системные демоны работали, когда обычные процессы уже не могут писать. Не рассчитывай на этот запас: процессы от root могут занять и его.
Заодно проверь, что диск не отвалился, а именно заполнился. Набор из мониторинга в базе:
dmesg | tail -40 | grep -iE 'error|fail|oom|bus|I/O'Шаг 2. Кто занял место
Самые тяжёлые каталоги, не выходя за пределы корневой ФС (-x не даёт du уйти в /proc и примонтированные диски):
du -xh -d 1 / 2>/dev/null | sort -h | tail -15Спускаешься глубже в самый толстый каталог и повторяешь. Обычные подозреваемые: /var/lib/docker, /var/log, /var/cache/apt, домашние каталоги с бэкапами.
Крупные файлы одним списком:
find / -xdev -type f -size +200M -printf '%s\t%p\n' 2>/dev/null | sort -n | tail -20Удобнее всего ncdu: интерактивное дерево, сортировка по размеру, удаление клавишей d с подтверждением.
apt install ncdu
ncdu -x /Лайфхак
На боевом сервере запускай ncdu -r -x /: -r отключает удаление, и случайное нажатие d ничего не снесёт. Если места нет совсем и apt install не проходит, обходись du.
journald
Системный журнал по умолчанию занимает до 10% файловой системы, но не больше 4 ГБ (SystemMaxUse). На маленьком диске это ощутимо.
journalctl --disk-usageСрочно ужать:
journalctl --rotate --vacuum-size=200M
# или оставить только последнюю неделю
journalctl --rotate --vacuum-time=7d--vacuum-* удаляет только архивные файлы журнала, активные не трогает. Поэтому вместе с ним нужен --rotate: он архивирует текущие файлы, и чистка работает по полной.
Чтобы не повторилось, задай лимит через drop-in, а не правкой основного файла (так рекомендует man journald.conf):
mkdir -p /etc/systemd/journald.conf.d[Journal]
SystemMaxUse=300M
MaxRetentionSec=1monthsystemctl restart systemd-journaldПерезапуск journald безопасен, это прямо сказано в документации. SystemMaxUse действует на постоянный журнал в /var/log/journal. Если задан и SystemKeepFree, journald соблюдает оба лимита и берёт меньший.
Логи Docker-контейнеров
Драйвер json-file по умолчанию пишет без ограничения размера. Это главный кандидат на сервере с ботом или панелью.
docker system df логи не показывает. Размер лога конкретного контейнера:
du -h "$(docker inspect --format='{{.LogPath}}' remnawave_bot)"Все контейнеры сразу, самые толстые внизу:
docker ps -aq | xargs docker inspect --format='{{.LogPath}}' | xargs du -h 2>/dev/null | sort -hПочему не rm
Файл лога держит открытым демон Docker. rm убирает только имя: по man unlink(2) файл продолжает существовать, пока закрыт не последний дескриптор. Место не освободится, а лог станет невидимым для du. Это классическая ситуация «удалил 5 ГБ, а df не изменился» (см. ниже про lsof +L1).
Если место нужно прямо сейчас, файл обнуляют на месте, не удаляя:
truncate -s 0 "$(docker inspect --format='{{.LogPath}}' remnawave_bot)"Грабли
Документация Docker прямо предупреждает: файлы json-file предназначены только для демона, вмешательство внешних инструментов может привести к непредсказуемому поведению. truncate здесь аварийная мера, а не регламент. Правильное решение: лимит max-size/max-file и пересоздание контейнеров. Как это сделать, в гайде
Docker для панелей и ботов.
Образы, кэш сборки, тома
Старые образы копятся после каждого docker compose pull, в сообществе это одна из типовых причин забитого диска (
ошибки и фиксы).
docker system df
docker system df -vКолонка RECLAIMABLE показывает, сколько можно вернуть. Дальше по нарастающей:
docker image prune # dangling-образы (<none>)
docker builder prune # кэш сборки: бот с build: копит его на каждой пересборке
docker image prune -a # все образы, которые не использует ни один контейнерНе делай так
docker system prune -a --volumes удаляет остановленные контейнеры, неиспользуемые сети, все неиспользуемые образы, кэш сборки и анонимные тома. Остановленный на время стек после этого придётся качать или собирать заново, а сеть, созданная остановленным проектом, пропадёт. Перед запуском посмотри docker ps -a и убедись, что нужное запущено.
apt
Кэш скачанных пакетов лежит в /var/cache/apt/archives/:
apt-get clean
apt-get autoremove --purgeclean удаляет все скачанные .deb из кэша. autoremove удаляет пакеты, которые ставились как зависимости и больше не нужны, --purge заодно их конфиги. Посмотри список перед подтверждением: на серверах, где что-то собирали руками, туда иногда попадает нужное.
Удалённые, но открытые файлы
Удалил большой лог, а df показывает то же самое. Файл ещё открыт процессом, и место вернётся только после закрытия дескриптора. Найти такие файлы:
lsof +L1+L1 выбирает открытые файлы, у которых не осталось ни одного имени в ФС. В колонке SIZE/OFF размер, в COMMAND и PID процесс, который держит файл. Лечится перезапуском этого процесса: systemctl restart <сервис> или docker restart <контейнер>. Дескриптор закроется, и место вернётся.
Урок на будущее: лог, в который кто-то пишет, не удаляют, а обнуляют (truncate -s 0 файл) или отдают logrotate.
logrotate: чтобы свои логи не росли
Логи сервисов, которые пишут в файлы (nginx, Xray с access.log, свои скрипты), ротирует logrotate. Он запускается ежедневно по таймеру или cron и читает конфиги из /etc/logrotate.d/.
/var/log/remnanode/*.log {
daily
rotate 7
maxsize 200M
compress
delaycompress
missingok
notifempty
copytruncate
}maxsizeротирует раньше срока, если файл вырос больше порога.copytruncateкопирует лог и обнуляет оригинал на месте. По man logrotate нужен, когда программе нельзя сказать закрыть лог. Строки, записанные между копированием и обнулением, могут потеряться.- Если сервис умеет переоткрывать логи, вместо
copytruncateиспользуйpostrotate ... endscriptс командой перезагрузки.
Проверить конфиг, ничего не меняя, и потом прогнать принудительно:
logrotate -d /etc/logrotate.d/remnanode
logrotate -f /etc/logrotate.d/remnanodeЧек-лист на будущее
- Лимит логов Docker в
/etc/docker/daemon.jsonи пересозданные после этого контейнеры. SystemMaxUseдля journald в drop-in.- logrotate на всё, что пишет в
/var/logмимо journald. docker image pruneпосле обновлений.- Алерт на заполнение диска раньше, чем он станет 100%. Как собрать простой мониторинг, в гайде мониторинг на коленке и в базе: мониторинг, скрипты мониторинга.