В первые минуты инцидента важнее всего не скорость, а порядок. Когда сыплются алерты, руки сами тянутся перезапустить сервис или что‑нибудь удалить. А причину быстрее находит тот, кто проходит одни и те же шаги в одном и том же порядке.

Ниже мой чек‑лист для Linux‑сервера: пять шагов, по нескольку команд на каждый. Я собрал его, пока делал 13 кейсов для тренажёра дежурств (о нём была первая статья). Поэтому у каждого шага есть пример из кейса, где именно он находит причину.

За пять минут чинить не обязательно. Задача этих минут — понять, в каком слое проблема, и ничего не сделать хуже.

Алерт говорит «сайт не работает», но «не работает» бывает разным. Первым делом сделайте тот же запрос, что и пользователь, а потом тот же запрос напрямую к приложению, мимо nginx:

curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://shop.example.ru/health
curl -sS -v http://127.0.0.1:8080/health

Код ответа уже называет слой, в котором стоит искать:

  • 502 или 504 — nginx жив, а приложение за ним не отвечает или слушает не тот адрес;

  • 500 — приложение работает, но падает на запросе: часто виновата база, диск или что‑то ещё снаружи;

  • таймаут — сервер перегружен или запрос не доходит по сети;

  • ошибка TLS — сертификат или, что неочевидно, часы сервера.

В кейсе про 502 nginx запущен и принимает соединения, и тот, кто начинает с «перезапущу nginx», теряет время. Запрос напрямую на адрес приложения сразу показывает, что проблема между nginx и приложением, а не в самом nginx.

Шаг 2. Ресурсы сервера

Процессор, память и диск проверяются за минуту, и в пяти из 13 моих кейсов след виден уже здесь. Поэтому смотрю их до логов приложения:

uptime                               # load average против числа ядер (nproc)
top -o %CPU                          # кто ест процессор; в top клавиша M — по памяти
free -h                              # смотрите на available, а не на free
sudo dmesg -T | grep -i 'out of memory'   # убивал ли кого-то OOM Killer
df -h                                # место
df -i                                # inode — про них забывают чаще всего

Два случая, где этот шаг легко пройти мимо:

  • «No space left on device», а df -h показывает 70% свободного места. Кончились не байты, а inode: кто‑то наплодил миллионы мелких файлов. Их найдёт sudo du --inodes -s /srv/app/* | sort -n.

  • Сервис падает сам по себе, в его логах пусто. Его убило ядро из‑за нехватки памяти, и след остался только в dmesg. Убитый процесс при этом часто не тот, кто съел память: виновника покажет ps aux --sort=-%mem | head.

    Шаг 3. Сервис и его журнал

    Если ресурсы в порядке, спрашиваем сам сервис, что с ним:

    systemctl status shop-app                          # запущен ли, код выхода, счётчик рестартов
    journalctl -u shop-app --since "30 min ago" --no-pager
    systemctl cat shop-app                             # юнит вместе со всеми drop-in


    В журнале ищите первую ошибку, а не последнюю. Сервис с Restart=always падает по кругу, и конец журнала забит одинаковыми последствиями. Причина обычно в строке перед первым падением.

    systemctl cat недооценен. Он показывает не только юнит, но и все drop‑in из *.service.d/, которые мог подложить деплой или скрипт «усиления безопасности». В кейсе про «Too many open files» один из вариантов именно такой: лимит дескрипторов снизил drop‑in, о котором никто не помнит. Реальный лимит работающего процесса покажет /proc/<PID>/limits.

    У некоторых сервисов свой лог помимо журнала: PostgreSQL пишет в /var/log/postgresql/, nginx — в /var/log/nginx/error.log. В кейсе с упавшим PostgreSQL ошибка в его логе сразу называет параметр и файл, который добавил деплой.

    Шаг 4. Сеть и зависимости

    Сервис запущен и в журнале жалуется на подключение? Значит, проблема между ним и тем, к чему он ходит:

    sudo ss -ltnxp                      # кто что слушает: TCP-порты и unix-сокеты
    getent hosts gateway.pay.internal   # имя так, как его ищет приложение
    dig gateway.pay.internal            # что отвечает DNS
    pg_isready -h 127.0.0.1             # жива ли база
    timedatectl                         # синхронизированы ли часы


    Сравнивайте не «работает / не работает», а то, куда ходит приложение, с тем, где сервис на самом деле:

    • В кейсе про 502 nginx идёт на один порт, а приложение после релиза слушает другой. Адрес из строки upstream в error.log и вывод ss не совпадают — причина найдена.

    • В кейсе с платёжным шлюзом dig отвечает правильно, а getent — нет: после скрипта харденинга в /etc/nsswitch.conf осталось hosts: files. dig спрашивает DNS напрямую, а приложение ищет имя через libc, как getent.

    • Ошибки TLS к чужому API и «token used before issued» в одном журнале указывают не на сертификаты и не на авторизацию, а на часы сервера. Хватает одного timedatectl.

      Шаг 5. Что менялось

      Самый полезный вопрос на инциденте — «что поменялось перед тем, как сломалось?». Релиз, правка конфига, обновление пакетов, чей‑то скрипт. В большинстве моих кейсов причина именно такая, как и на настоящих серверах.

      sudo find /etc /opt -type f -newermt '2 hours ago'   # что правили за последние 2 часа
      tail -n 30 /var/log/apt/history.log                  # что ставили и обновляли
      last -n 10                                           # кто заходил на сервер
      kubectl rollout history deployment/api               # если дело в Kubernetes

      Если изменение нашлось и по времени совпадает с алертом, откат почти всегда быстрее и безопаснее, чем подбор исправления посреди ночи. Разбираться, что именно в релизе не так, можно утром.

      Но откат помогает, только если изменение прошло через деплой. В кейсе про CrashLoopBackOff есть вариант, где ConfigMap поправили руками: kubectl rollout undo вернёт предыдущую версию деплоймента, но поды продолжат падать с тем же сломанным конфигом.

      Чего не делать

      Первые минуты чаще всего портят не незнанием команд, а тремя рефлексами.

      • Перезапускать всё подряд. Рестарт стирает улики: процесс, который тек, его открытые файлы и соединения. А сервис со сломанным конфигом после рестарта просто не поднимется, и к старой проблеме добавится новая. Сначала status и журнал, потом осознанный рестарт.

      • Удалять логи вслепую. rm лога, который держит открытым работающий процесс, места не освободит: файл исчезнет из ls, но останется на диске (такие файлы покажет sudo lsof +L1). Обнуляйте его через truncate -s 0. И смотрите, что чистите: в кейсе с забитым /var/log рядом с распухшим логом лежит auth.log, а аудит удалять нельзя.

      • Убивать виновника и считать дело сделанным. Если процесс держит systemd или сторожевой скрипт, через секунду он вернётся с теми же параметрами. В кейсе про OOM Killer kill помогает на несколько секунд: сторож поднимает процесс заново, и память снова уходит.

      И ещё один простой приём: записывайте, что делаете и во сколько. Даже если вы один, через час не вспомните, после какой команды метрики пошли вниз, а для постмортема это главное.

      Это мой порядок, и наверняка не единственный. Какие команды вы набираете первыми, когда заходите на упавший сервер? Самые полезные добавлю в чек‑лист.

Комментарии (2)


  1. Granulex
    26.10.2026 14:32

    С df -h бывает ровно наоборот: старые логи удалены – диск всё ещё полный, и место не вернётся, пока процесс, держащий удалённый файл открытым, не перезапустят. Ловится одной командой lsof +L1. Вместе с df -i из статьи это закрывает обе стороны ошибки "No space left on device".


    1. verin_is Автор
      26.10.2026 14:32

      Спасибо, точное дополнение. В статье это есть в разделе «Чего не делать»: rm лога, который держит процесс, места не вернёт, а lsof +L1 такие файлы покажет. Добавлю, что перезапускать процесс не обязательно: место можно вернуть, обнулив удалённый файл через его дескриптор - : > /proc/<PID>/fd/<N>, где PID и номер дескриптора берутся из вывода lsof +L1 (колонка FD, число без буквы).