Эксперт по Linux терминалу
Эксперт по Linux-терминалу помогает выполнить задачу командной строки с разбором команд, скриптами и устранением неполадок.
Пример запуска
Сгенерировано Claude Opus 5.5, 23 сентября 2026 г.
Значения переменных
- Дистрибутив linux
- Ubuntu 22.04 LTS
- Оболочка
- bash 5.1
- Уровень пользователя
- средний (уверенно работает в терминале, но не администрировал серверы)
- Окружение
- удалённый VPS-сервер (2 vCPU, 4 ГБ RAM), доступ по SSH, на сервере крутится Nginx и PostgreSQL 14
- Запрос или задача
- Диск на сервере заполнен на 95%. Нужно найти, что съедает место в каталоге /var/log и /var/lib/postgresql, безопасно очистить старые логи и ротировать их через logrotate, чтобы такая ситуация не повторялась. Важно не задеть текущие файлы БД и не сломать работающий Nginx.
- Тон
- деловой, но дружелюбный, без лишней воды
Диск заполнен на 95%: диагностика и очистка /var/log и /var/lib/postgresql, настройка logrotate
1. Уточнение цели
Задача:
- Найти, что занимает место в
/var/logи/var/lib/postgresql. - Безопасно освободить место.
- Настроить ротацию логов, чтобы диск не заполнялся снова.
Ограничения: файлы БД не трогаем, Nginx не останавливаем.
Предположения:
/varлежит на корневом разделе/, файловая система ext4.- PostgreSQL 14 установлен из пакетов Ubuntu. Кластер
14/main, данные в/var/lib/postgresql/14/main, логи в/var/log/postgresql/. - Nginx установлен из пакетов Ubuntu, поэтому уже есть
/etc/logrotate.d/nginx. - logrotate на 22.04 запускается через
logrotate.timer(systemd), а не через cron.
Два вопроса, которые могут изменить план. Пока исхожу из ответа «нет» на оба:
- Есть ли репликация, архивирование WAL или агент бэкапов (pgBackRest, wal-g, Barman)? От этого зависит, что делать, если разросся каталог
pg_wal. - Нужно ли хранить логи дольше N дней по требованиям (аудит, аналитика, 152-ФЗ и т. п.)? Ниже я закладываю 14 дней. Если нужно дольше, логи стоит выгружать на другой носитель, а не удалять.
⚠️ Главное правило: в
/var/lib/postgresql/14/main(особенно вpg_wal/иbase/) ничего не удаляем руками. Удаление WAL-файлов вручную — самый частый способ безвозвратно потерять базу. Место там освобождается только средствами самого PostgreSQL.
2. Диагностика
Шаг 2.1. Общая картина по диску
df -h /
df -i /
- Назначение: показать занятость раздела по объёму и по inode.
- Флаги:
-hвыводит размеры в человекочитаемом виде.-iпоказывает inode вместо байтов: диск может «кончиться» из-за миллионов мелких файлов даже при свободных гигабайтах. - Ожидаемо: строка вида
/dev/vda1 40G 38G 2.0G 95% /. ЕслиIUse%близок к 100%, проблема в количестве файлов, а не в объёме.
Шаг 2.2. Кто занимает место в /var
sudo du -xh --max-depth=1 /var 2>/dev/null | sort -h
sudo du -xh --max-depth=2 /var/log 2>/dev/null | sort -h | tail -15
sudo du -xh --max-depth=3 /var/lib/postgresql 2>/dev/null | sort -h | tail -15
- Назначение: найти самые тяжёлые каталоги.
- Флаги:
-xне выходит за пределы файловой системы (не посчитает смонтированные диски).-hдаёт человекочитаемые размеры,--max-depth=Nзадаёт глубину.sort -hсортирует с учётом суффиксов K/M/G.2>/dev/nullскрывает ошибки доступа.
- Зачем
sudo: каталоги/var/log/*и/var/lib/postgresqlзакрыты для обычного пользователя, без правduпокажет заниженные цифры. - Ожидаемо: внизу списка окажутся самые крупные каталоги, например
/var/log/nginx,/var/log/journalили/var/lib/postgresql/14/main/pg_wal.
Шаг 2.3. Самые крупные файлы в логах
sudo find /var/log -xdev -type f -size +50M -printf '%s\t%TY-%Tm-%Td\t%p\n' | sort -n | numfmt --field=1 --to=iec
- Назначение: найти конкретные файлы крупнее 50 МБ с датой изменения.
- Разбор:
-size +50Mотбирает файлы крупнее 50 МиБ.-printfзадаёт формат вывода:%s— размер в байтах,%T…— дата изменения,%p— путь.numfmt --to=iecпревращает байты в1.2G.
- Ожидаемо: например,
3.4G 2026-09-23 /var/log/nginx/access.log. Если активный лог весит гигабайты, ротация для него не работает.
Шаг 2.4. Журнал systemd
journalctl --disk-usage
- Ожидаемо:
Archived and active journals take up 2.1G in the file system.По умолчанию journald может занять до 10% раздела (но не более 4 ГБ).
Шаг 2.5. «Призрачные» файлы: удалены, но ещё открыты
sudo lsof -nP +L1 2>/dev/null | awk 'NR==1 || $7 > 10485760'
- Назначение: найти файлы, которые кто-то уже удалил через
rm, но процесс (Nginx, Postgres) продолжает держать их открытыми. Место на диске освобождается только после закрытия файла. - Флаги:
+L1отбирает файлы с числом ссылок меньше 1, то есть удалённые.-nPотключает резолв имён хостов и портов (так быстрее).awkоставляет заголовок и файлы крупнее 10 МБ (7-я колонка — размер).
- Ожидаемо: пустой вывод. Если есть строки с
(deleted), тоdfиduрасходятся, и нужно переоткрыть логи у процесса (см. шаг 3.4).
Шаг 2.6. PostgreSQL: что именно растёт
Размеры баз:
sudo -u postgres psql -X -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size FROM pg_database ORDER BY pg_database_size(datname) DESC;"
sudo -u postgres: подключение идёт через peer-аутентификацию от системного пользователяpostgres, пароль не нужен.-X: не читать~/.psqlrc, чтобы вывод был предсказуемым.
Ключевые настройки:
sudo -u postgres psql -X -c "SHOW max_wal_size;" -c "SHOW wal_keep_size;" -c "SHOW archive_mode;" -c "SHOW logging_collector;" -c "SHOW log_directory;" -c "SHOW log_min_duration_statement;" -c "SHOW log_statement;"
- Смотрим:
logging_collector = on: логи пишутся внутрь data-каталога (14/main/log/), и системный logrotate их не видит.log_statement = allилиlog_min_duration_statement = 0: в лог попадает каждый запрос, это верный путь к гигабайтам логов.
Если большой каталог pg_wal, проверьте слоты репликации и архиватор:
sudo -u postgres psql -X -c "SELECT slot_name, slot_type, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots;"
sudo -u postgres psql -X -c "SELECT archived_count, failed_count, last_failed_wal, last_failed_time FROM pg_stat_archiver;"
- Ожидаемо: слотов нет или все
active = t, аfailed_countне растёт. - Тревожные признаки:
- Слот с
active = fиretained_walв гигабайтах. PostgreSQL держит WAL для реплики, которой больше нет. - Растущий
failed_countприarchive_mode = on.archive_commandпадает, и WAL копятся.
- Слот с
Самые крупные таблицы (подставьте имя базы):
sudo -u postgres psql -X -d ИМЯ_БД -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;"
💡 Проверьте ещё домашний каталог пользователя
postgres, то есть сам/var/lib/postgresql. Туда часто складывают дампыpg_dumpпо cron, и они копятся годами. Их можно удалять (после проверки), это не файлы БД.
3. Очистка
Шаг 3.0. Если диск заполнен на 100% (аварийный режим)
При полном диске падают sudo, редакторы и сам PostgreSQL. Сначала освободите немного места безопасными командами:
sudo apt-get clean
sudo journalctl --vacuum-size=200M
apt-get cleanудаляет кэш скачанных.debиз/var/cache/apt/archives. Это безопасно: пакеты при необходимости скачаются заново.--vacuum-size=200Mудаляет архивные файлы журнала, пока общий объём не станет не больше 200 МБ. Активный журнал не трогается.- Ожидаемо:
Vacuuming done, freed 1.9G of archived journals from /var/log/journal/....
Шаг 3.1. Старые ротированные логи: сначала dry-run
sudo find /var/log -xdev -type f \( -name '*.gz' -o -name '*.[0-9]' -o -name '*.old' \) -mtime +14 -printf '%TY-%Tm-%Td %8k KB %p\n' | sort
- Назначение: показать, что будет удалено. Это только вывод, ничего не удаляется.
- Разбор:
\( … -o … \)— группа условий «ИЛИ». Скобки экранированы, чтобы bash не принял их за свой синтаксис.*.gz— сжатые архивы,*.[0-9]— нумерованные копии вродеsyslog.1,*.old— старые копии.-mtime +14отбирает файлы, изменённые больше 14 полных суток назад.- Активные
*.logне попадают в выборку: у них нет таких суффиксов.
- Ожидаемо: список вида
2026-08-01 51234 KB /var/log/nginx/access.log.15.gz.
Если список устраивает, удаляем:
sudo find /var/log -xdev -type f \( -name '*.gz' -o -name '*.[0-9]' -o -name '*.old' \) -mtime +14 -print -delete
⚠️ Необратимо.
-deleteудаляет без корзины. Всегда запускайте сначала вариант с-printf/-deleteставьте в конец выражения: в начале он удалит всё подряд до применения фильтров.
Шаг 3.2. Огромный активный лог Nginx
Если access.log или error.log весит гигабайты, не удаляйте его через rm. Nginx продолжит писать в открытый дескриптор, и место не освободится (см. шаг 2.5).
Вариант А (предпочтительный): принудительная ротация штатным конфигом.
sudo logrotate -f -v /etc/logrotate.d/nginx
-fротирует принудительно, даже если по расписанию ещё рано.-vвключает подробный вывод.- После ротации logrotate выполнит
postrotate. Nginx получит сигналUSR1и переоткроет логи без перезапуска и без обрывов соединений. - Ожидаемо: строки
rotating log /var/log/nginx/access.log…иrunning postrotate script. - ⚠️ При
delaycompressсвежая копияaccess.log.1остаётся несжатой. Место освободится после следующей ротации, либо удалите или сожмите её вручную.
Вариант Б (экстренный): обнулить файл на месте.
sudo truncate -s 0 /var/log/nginx/access.log
truncate -s 0обрезает файл до нуля байт. Дескриптор у Nginx остаётся валидным, место освобождается сразу.- ⚠️ Необратимо: содержимое лога теряется. Если логи нужны, сначала сохраните сжатую копию на другой носитель, например
sudo gzip -c /var/log/nginx/access.log > /mnt/backup/access-$(date +%F).log.gz.
Шаг 3.3. Логи PostgreSQL
Сначала смотрим размеры:
sudo ls -lh /var/log/postgresql/
sudo ls -lh /var/lib/postgresql/14/main/log/ 2>/dev/null
- Логи в
/var/log/postgresql/(стандарт Ubuntu,logging_collector = off): старые*.gz/*.1уже удалены на шаге 3.1. Активный файл можно обрезать черезtruncate, как в варианте Б. - Логи в
14/main/log/(logging_collector = on): удалять можно только файлы логов в подкаталогеlog/, и только старые:
sudo find /var/lib/postgresql/14/main/log -maxdepth 1 -type f -name 'postgresql-*.log' -mtime +7 -print
# после проверки списка — то же самое с -delete вместо -print
-maxdepth 1не даётfindуйти глубже каталогаlog/.- ⚠️ Проверьте путь дважды. Опечатка вида
/var/lib/postgresql/14/main -name ...удалит файлы БД.
Шаг 3.4. Освободить место, занятое «удалёнными» файлами
Если на шаге 2.5 нашлись файлы (deleted):
sudo systemctl reload nginx # nginx переоткроет логи, мягкая операция
sudo -u postgres psql -X -c "SELECT pg_rotate_logfile();" # только при logging_collector = on
reloadперечитывает конфиг и переоткрывает логи без обрыва текущих соединений. Не используйтеrestartбез необходимости. Передreloadможно проверить конфиг:sudo nginx -t.- Для других процессов обычно помогает перезапуск соответствующей службы. Делайте его в согласованное окно.
Шаг 3.5. Если разросся pg_wal
- Неактивный слот репликации. Если реплика точно больше не нужна, удалите слот. Это необратимо: реплика, использовавшая слот, потеряет возможность догнать мастера.
sudo -u postgres psql -X -c "SELECT pg_drop_replication_slot('ИМЯ_СЛОТА');" - Падает
archive_command. Смотрите причину в логе PostgreSQL:grep -i archive /var/log/postgresql/postgresql-14-main.log | tail. Чините команду (часто переполнено место назначения или нет прав). - В обоих случаях PostgreSQL сам удалит лишние WAL после ближайшего checkpoint. Ускорить можно так:
sudo -u postgres psql -X -c "CHECKPOINT;".
4. Настройка ротации, чтобы проблема не повторилась
Шаг 4.1. Проверить, работает ли logrotate вообще
systemctl list-timers logrotate.timer
sudo journalctl -u logrotate.service --since "7 days ago" --no-pager | tail -30
sudo grep -E 'nginx|postgresql' /var/lib/logrotate/status
- Ожидаемо:
- таймер активен, следующий запуск в ближайшие сутки;
- в журнале нет строк
error:; - в state-файле есть свежие даты для логов Nginx и PostgreSQL.
- Если дат нет или они старые, ротация не происходила. Причины разобраны в разделе 6.
Шаг 4.2. Бэкап конфигов
sudo cp -a /etc/logrotate.d/nginx /etc/logrotate.d/nginx.bak-$(date +%F)
sudo cp -a /etc/logrotate.d/postgresql-common /etc/logrotate.d/postgresql-common.bak-$(date +%F)
cp -aсохраняет права, владельца и время изменения.- ⚠️ Важно: logrotate читает все файлы в
/etc/logrotate.d/, включая бэкапы. Это вызовет ошибкуduplicate log entry. Сразу перенесите бэкапы в другое место:
sudo mkdir -p /root/logrotate-backup
sudo mv /etc/logrotate.d/*.bak-* /root/logrotate-backup/
Шаг 4.3. Конфиг для Nginx
sudo nano /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
maxsize 200M
rotate 14
missingok
notifempty
compress
delaycompress
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
Что изменилось относительно стандартного конфига Ubuntu: добавлен maxsize 200M, rotate выставлен в 14.
Директивы:
daily— ротировать раз в сутки.maxsize 200M— ротировать и раньше, если файл превысил 200 МБ. Работает только при запуске logrotate чаще раза в сутки (шаг 4.5).rotate 14— хранить 14 архивов, более старые удаляются автоматически.compress/delaycompress— сжимать gzip, но самую свежую копию (.1) оставлять несжатой, пока Nginx гарантированно в неё не пишет.create 0640 www-data adm— права и владелец нового файла.sharedscripts— выполнитьpostrotateодин раз на все файлы, а не на каждый.postrotate … invoke-rc.d nginx rotate— отправить Nginx сигналUSR1на переоткрытие логов.
⚠️ Если в
nginx.confлоги пишутся в подкаталоги (/var/log/nginx/site1/access.log) или вообще вне/var/log/nginx, маска*.logих не охватит. Проверьте фактические пути:sudo nginx -T 2>/dev/null | grep -E '^\s*(access|error)_log' | sort -uДля таких путей добавьте их в первую строку конфига через пробел, например
/var/log/nginx/*.log /var/log/nginx/*/*.log {.
ℹ️ Файл
/etc/logrotate.d/nginxпринадлежит пакету (conffile). При обновлении nginxaptспросит, оставить вашу версию или взять новую. Выбирайте «оставить» (N).
Шаг 4.4. Конфиг для PostgreSQL
sudo nano /etc/logrotate.d/postgresql-common
/var/log/postgresql/*.log {
daily
maxsize 200M
rotate 7
copytruncate
compress
delaycompress
notifempty
missingok
su root root
}
copytruncateкопирует лог и обрезает оригинал. PostgreSQL безlogging_collectorне умеет переоткрывать лог по сигналу, поэтому здесь используется именно этот режим. Минус: несколько строк, записанных в момент копирования, могут потеряться. Для логов это допустимо.su root root— от чьего имени выполнять ротацию. Без этой директивы logrotate откажется работать с каталогом, если сочтёт его права небезопасными.
Если logging_collector = on. Системный logrotate тут не нужен, ротацией управляет сам PostgreSQL. Настройки в /etc/postgresql/14/main/postgresql.conf:
log_filename = 'postgresql-%a.log' # имя по дню недели: Mon, Tue, ...
log_rotation_age = 1d
log_rotation_size = 0 # не ротировать по размеру, иначе truncate сотрёт часть дня
log_truncate_on_rotation = on # перезаписывать файл недельной давности
- Такая схема даёт кольцо из 7 файлов, которое никогда не растёт бесконечно.
- Применение без перезапуска БД:
sudo systemctl reload postgresql@14-main. Все четыре параметра применяются по reload (контекстsighup); это можно проверить запросомSELECT name, context FROM pg_settings WHERE name LIKE 'log_%';.
Проверьте и уровень логирования: log_statement = 'all' на проде почти всегда ошибка. Разумный вариант — log_min_duration_statement = 1000, то есть логировать запросы дольше 1 секунды.
Шаг 4.5. Запускать logrotate каждый час (чтобы работал maxsize)
sudo systemctl edit logrotate.timer
В открывшемся редакторе добавьте:
[Timer]
OnCalendar=
OnCalendar=hourly
Затем примените изменения и проверьте расписание:
sudo systemctl daemon-reload
systemctl list-timers logrotate.timer
- Пустая строка
OnCalendar=сбрасывает исходное значениеdaily. Без неё таймеры сложатся. dailyв самих конфигах по-прежнему означает «не чаще раза в сутки». Почасовой запуск лишь даётmaxsizeшанс сработать раньше.- Ожидаемо: в колонке
NEXTстоит время в пределах ближайшего часа.
Шаг 4.6. Ограничить journald
sudo mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=300M\nSystemKeepFree=1G\n' | sudo tee /etc/systemd/journald.conf.d/size.conf
sudo systemctl restart systemd-journald
SystemMaxUse— потолок размера журнала.SystemKeepFree— сколько места journald обязан оставлять свободным.sudo teeиспользуется потому, что перенаправление>выполняется оболочкой без sudo. Конструкцияsudo echo … > /etc/…упадёт сPermission denied.- Перезапуск
systemd-journaldбезопасен, сервисы не затрагиваются.
Шаг 4.7. Проверка конфигов
sudo logrotate -d /etc/logrotate.d/nginx
sudo logrotate -d /etc/logrotate.d/postgresql-common
-d— режим отладки. Ничего не меняет, только показывает, что logrotate сделал бы. Это и есть dry-run для logrotate.- Ожидаемо: строки
considering log /var/log/nginx/access.log,log does not need rotatingилиlog needs rotating, без строкerror:.
Затем боевой прогон и проверка Nginx:
sudo logrotate -f -v /etc/logrotate.d/nginx
systemctl status nginx --no-pager
ls -lh /var/log/nginx/
- Ожидаемо: Nginx в состоянии
active (running), появился новый пустой или маленькийaccess.logи копияaccess.log.1.
5. Скрипт: диагностика и очистка с dry-run по умолчанию
Скрипт объединяет шаги 2.4, 2.5, 3.1 и 3.2. Он не трогает /var/lib/postgresql и активные *.log. Без флага --apply ничего не удаляет.
#!/usr/bin/env bash
# clean-logs.sh — безопасная очистка старых логов в /var/log.
# По умолчанию: dry-run (только показывает). Реальное удаление: sudo ./clean-logs.sh --apply
# Настройка через окружение: RETENTION_DAYS=30 JOURNAL_MAX=500M sudo -E ./clean-logs.sh
set -euo pipefail
readonly LOG_DIR="/var/log"
readonly RETENTION_DAYS="${RETENTION_DAYS:-14}" # удалять архивы старше N суток
readonly JOURNAL_MAX="${JOURNAL_MAX:-200M}" # целевой размер журнала systemd
APPLY=0
log() { printf '[%s] %s\n' "$(date +%T)" "$*"; }
die() { printf 'ОШИБКА: %s\n' "$*" >&2; exit 1; }
usage(){ echo "Использование: $0 [--apply]"; }
trap 'rc=$?; echo "ОШИБКА: строка $LINENO, код возврата $rc" >&2' ERR
for arg in "$@"; do
case "$arg" in
--apply) APPLY=1 ;;
-h|--help) usage; exit 0 ;;
*) usage >&2; exit 2 ;;
esac
done
[[ $EUID -eq 0 ]] || die "нужны права root: запустите через sudo"
[[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]] || die "RETENTION_DAYS должно быть числом"
[[ -d "$LOG_DIR" ]] || die "каталог $LOG_DIR не найден"
(( APPLY )) && log "РЕЖИМ: УДАЛЕНИЕ" || log "РЕЖИМ: dry-run (ничего не удаляется)"
log "Свободно на /: $(df -h --output=avail / | tail -1 | tr -d ' ')"
# --- 1. Ротированные архивы старше RETENTION_DAYS. Активные *.log не подходят под маски.
FIND_ARGS=( "$LOG_DIR" -xdev -type f
'(' -name '*.gz' -o -name '*.xz' -o -name '*.[0-9]' -o -name '*.old' ')'
-mtime "+$RETENTION_DAYS" )
count=$(find "${FIND_ARGS[@]}" -printf '.' | wc -c)
bytes=$(find "${FIND_ARGS[@]}" -printf '%s\n' | awk '{s+=$1} END {print s+0}')
log "Архивов старше ${RETENTION_DAYS} дн.: ${count} шт., $(numfmt --to=iec "$bytes")"
if (( count > 0 )); then
if (( APPLY )); then
find "${FIND_ARGS[@]}" -print -delete
else
find "${FIND_ARGS[@]}" -printf ' %TY-%Tm-%Td %10s %p\n' | sort
fi
fi
# --- 2. Журнал systemd
log "journald: $(journalctl --disk-usage 2>&1)"
if (( APPLY )); then
journalctl --vacuum-size="$JOURNAL_MAX"
else
log "(dry-run) выполнил бы: journalctl --vacuum-size=$JOURNAL_MAX"
fi
# --- 3. Отчёт: удалённые, но открытые файлы (только диагностика, ничего не меняет)
if command -v lsof >/dev/null 2>&1; then
log "Удалённые, но удерживаемые процессами файлы (>10 МБ):"
lsof -nP +L1 2>/dev/null | awk 'NR==1 || $7 > 10485760' || true
fi
# --- 4. Отчёт: крупные активные логи (кандидаты на logrotate -f или truncate вручную)
log "Активные логи крупнее 100 МБ:"
find "$LOG_DIR" -xdev -type f -name '*.log' -size +100M -printf ' %10s %p\n' | sort -n || true
log "Свободно на / после: $(df -h --output=avail / | tail -1 | tr -d ' ')"
Запуск:
chmod +x clean-logs.sh
sudo ./clean-logs.sh # посмотреть, что будет сделано
sudo ./clean-logs.sh --apply # выполнить
Пояснения к реализации:
set -euo pipefail: скрипт завершается при любой ошибке, при обращении к необъявленной переменной и при сбое любой команды в конвейере.- Аргументы
findсобраны в массивFIND_ARGS. Поэтому dry-run и удаление гарантированно используют одинаковый фильтр, и не бывает ситуации «проверил одно, удалил другое». || trueстоит там, где пустой результат — это норма. Например,lsofвозвращает код 1, если ничего не нашёл, и без|| trueэто прервало бы скрипт.- В конвейерах нет
head: сpipefailон может вызвать ложное падение из-за SIGPIPE (код 141).
Регулярно запускать скрипт не нужно, эту работу берёт на себя logrotate. Скрипт предназначен для разовой уборки.
6. Устранение неполадок
| Симптом | Вероятная причина | Диагностика / решение |
|---|---|---|
No space left on device при sudo, nano, apt |
Диск заполнен на 100% | Шаг 3.0: apt-get clean, journalctl --vacuum-size, truncate крупнейшего лога |
df показывает 95%, а du намного меньше |
Удалённые, но открытые файлы | sudo lsof -nP +L1, затем systemctl reload соответствующей службы |
То же, но lsof пуст |
Резерв ext4 для root (5%) или файлы «под» точкой монтирования | sudo tune2fs -l /dev/vda1 | grep -i reserved (подставьте своё устройство) |
| Место есть, но файлы не создаются | Кончились inode | df -i. Ищите каталоги с тысячами мелких файлов: sudo find /var -xdev -type f | cut -d/ -f2-4 | sort | uniq -c | sort -n | tail |
error: skipping "…" because parent directory has insecure permissions |
Каталог лога доступен на запись группе или всем | Добавьте в блок su root adm (или владельца каталога) |
error: … duplicate log entry for … |
Один файл описан в двух конфигах (часто это забытый .bak в /etc/logrotate.d/) |
sudo grep -rn '/var/log/nginx' /etc/logrotate.d/ и перенос дубля |
После ротации Nginx пишет в access.log.1, а access.log пуст |
postrotate не отработал |
sudo logrotate -f -v … и смотреть вывод; sudo nginx -t; sudo systemctl reload nginx |
| logrotate не запускается | Таймер отключён или упал сервис | systemctl status logrotate.timer logrotate.service, journalctl -u logrotate.service. Код возврата: systemctl show -p ExecMainStatus logrotate.service |
| Ротация «не нужна», хотя файл огромный | Состояние в /var/lib/logrotate/status говорит, что ротация уже была сегодня |
Добавьте maxsize и почасовой таймер (шаг 4.5) или разово logrotate -f |
| PostgreSQL не стартует после заполнения диска | Не смог записать WAL или checkpoint | sudo journalctl -u postgresql@14-main -n 50, tail -50 /var/log/postgresql/postgresql-14-main.log. Освободите место вне data-каталога и запустите: sudo systemctl start postgresql@14-main |
pg_wal не уменьшается после исправлений |
Ещё не прошёл checkpoint | sudo -u postgres psql -X -c "CHECKPOINT;", затем повторите du |
Общий приём: у любой команды проверяйте код возврата через echo $? сразу после неё. 0 означает успех, всё остальное — повод читать вывод или man.
Полезно знать
ncdu— интерактивный анализатор диска (sudo apt install ncdu, затемsudo ncdu -x /var). Показывает то же, чтоdu | sort, но позволяет ходить по каталогам стрелками. Клавишаdудаляет файл, так что будьте внимательны.rmне освобождает место, пока файл открыт. Для активных логов используйтеtruncate -s 0или ротацию с переоткрытием. Для внезапных расхожденийdf/duпервым делом запускайтеlsof +L1.- Мониторинг вместо пожаротушения. Чтобы узнавать о проблеме заранее, а не на 95%, можно повесить в cron простую проверку:
df --output=pcent / | tail -1 | tr -dc '0-9'и уведомление при значении выше 80. Ещё лучше — node_exporter + Prometheus или любой внешний мониторинг VPS. - Логи Nginx можно писать сразу сжатыми и с буфером:
access_log /var/log/nginx/access.log combined gzip=5 buffer=64k flush=5m;. Это снижает объём и нагрузку на диск. Для статики и health-check'ов можно отключить логирование (access_log off;в нужномlocation). После правки обязательноsudo nginx -t && sudo systemctl reload nginx.
Похожие промпты
Ревью и рефакторинг PowerShell-скрипта
Разбирает существующий скрипт PowerShell: что сломается на реальных данных, где ошибки глушатся вместо обработки и что переписать в первую очередь, а что оставить.
Скрипт PowerShell под задачу
Пишет скрипт PowerShell с обработкой ошибок, логированием и режимом пробного прогона — так, чтобы его можно было запустить на рабочей машине и не бояться последствий.
Диагностика проблем VPN-подключения
Методично диагностирует проблемы VPN: соединение не устанавливается, скорость падает, утекает DNS — по одному шагу, с командами под вашу ОС.
Помощник по настройке WireGuard и OpenVPN
Пошагово разворачивает собственный VPN-сервер на WireGuard или OpenVPN: готовые команды и конфиги, firewall, клиентские профили и проверка утечек.