nginx.service в systemd: разбор отказов запуска
У nginx почти все отказы запуска ловятся до старта самого сервера: unit-файл вызывает nginx -t предварительной командой, и падение происходит на ней. Поэтому первая строка разбора — вывод проверки конфигурации, а не журнал службы.
О службе
В дистрибутивных unit-файлах nginx запускается с Type=forking и предварительной командой ExecStartPre=/usr/sbin/nginx -t. Такая проверка удобна: она отсекает ошибки в конфигурации до того, как старый процесс будет остановлен. Но она же меняет картину в журнале: код возврата приходит от проверки, и в статусе видна строка Control process exited, а не Main process.
Вторая особенность — работа с правами. Главный процесс nginx работает от root, чтобы занять порты 80 и 443, а рабочие процессы переходят на непривилегированного пользователя из директивы user в nginx.conf. Из-за этого ошибки доступа к файлам приходят от рабочих процессов, а не от службы: в журнале это строки с уровнем crit и alert, которые к самому systemd отношения не имеют.
Третья — перезагрузка настроек. systemctl reload nginx посылает сигнал HUP, и nginx поднимает новые рабочие процессы, не разрывая текущие соединения. Полный перезапуск (restart) обрывает соединения, поэтому для правки конфигурации почти всегда нужен именно reload. Если reload не применяет изменения, проверьте, что правили тот файл, который действительно включён.
Как устроена
| Тип unit | обычно Type=forking с PIDFile=/run/nginx.pid |
|---|---|
| Проверка конфигурации | nginx -t — она же стоит в ExecStartPre= и её код вы видите при отказе |
| Перезагрузка настроек | systemctl reload nginx — сигнал HUP, соединения не рвутся |
| Пользователь рабочих процессов | задаётся директивой user в nginx.conf, а не User= в unit-файле |
| Куда пишет ошибки | в error_log из nginx.conf; в журнал systemd попадает только то, что вышло до открытия файлов логов |
Частые ошибки
53 записи базы отмечены за этой службой.
Сообщения журнала
- Address already in use при запуске службыПорт или адрес уже занят: bind() failed (98: Address already in use). Как найти владельца порта и что делать с остатками прежнего процесса.
- Permission denied в журнале службыОтказ в доступе у службы systemd: права на файл, каталоги по пути, пользователь службы, параметры изоляции, SELinux и AppArmor.
- bind: Permission denied при привязке к портуОтказ при привязке к порту: обычно порт ниже 1024 у службы от непривилегированного пользователя. Как дать возможность CAP_NET_BIND_SERVICE.
- Broken pipe в журнале службыЗапись в закрытый канал или соединение: клиент отключился, обработчик завершился, конвейер разорван.
- Cannot assign requested address при привязкеОшибка 99 при привязке: адрес не принадлежит машине или ещё не поднят. Разбор с network-online.target и ip_nonlocal_bind.
- Connection reset by peer в журнале службыСоединение сброшено другой стороной: обрыв клиента, перезапуск сервера, промежуточное устройство.
- No such file or directory в журнале службыСлужба не находит файл или каталог. Разбор: путь, момент запуска, изоляция unit-файла, символические ссылки, приватный /tmp.
- SELinux: avc denied в журнале аудитаSELinux запретил операцию службе: как прочитать запись аудита, найти метку и исправить без отключения защиты.
- Temporary failure in name resolution в журнале службыСлужба не может разрешить имя: не готова сеть, нет сервера имён, мешает изоляция. Разбор при загрузке и в работе.
- certificate verify failed в журнале службыПроверка сертификата не прошла: истёк срок, неполная цепочка, неверное имя, нет доверенных корневых сертификатов.
- Служба не читает закрытый ключ TLSПрава на закрытый ключ: как дать доступ службе, не открывая ключ всем. Группы и каталоги сертификатов.
Коды выхода
- status=1/FAILURE в systemdКод 1/FAILURE означает, что программа запустилась и сама завершилась с ошибкой. Как найти настоящую причину в журнале службы.
- status=0/SUCCESS, но служба считается упавшейПрограмма завершилась успешно, а systemctl показывает inactive или failed. Разбор: Type=simple против forking, RemainAfterExit, демонизация.
- status=203/EXEC в systemdКод 203/EXEC означает, что systemd не смог выполнить программу из ExecStart=. Разбор причин: путь, права, интерпретатор, синтаксис оболочки.
- status=205/LIMITS в systemdКод 205/LIMITS: systemd не смог применить ограничения ресурсов из Limit*=. Обычно значение недопустимо или превышает жёсткий предел.
- status=217/USER в systemdКод 217/USER означает, что systemd не смог определить или сменить пользователя из User=. Разбор причин: пользователя нет, имя недопустимо, конфликт с DynamicUser.
- status=218/CAPABILITIES в systemdКод 218/CAPABILITIES: не удалось применить набор возможностей процесса из CapabilityBoundingSet= или AmbientCapabilities=.
- status=226/NAMESPACE в systemdКод 226/NAMESPACE: не удалось настроить пространства имён монтирования, UTS или IPC. Частая причина — путь в ReadOnlyPaths= или ProtectHome=.
- status=240/LOGS_DIRECTORY в systemdКод 240/LOGS_DIRECTORY: не удалось подготовить каталог журналов службы в /var/log из LogsDirectory=.
- status=4/NOPERMISSION в systemdКод 4/NOPERMISSION: программа сообщила о недостатке прав. По соглашению LSB это «у пользователя недостаточно привилегий».
Ресурсы и ограничения
- Too many open files в журнале службыСлужба исчерпала лимит файловых дескрипторов. Как правильно поднять LimitNOFILE и когда дело в утечке.
- kernel: TCP: request_sock … overflowСоединения теряются при наплыве: переполнена очередь ожидающих соединений. Как считать somaxconn и backlog.
- nf_conntrack: table full, dropping packetСоединения обрываются под нагрузкой: заполнена таблица отслеживания соединений ядра.
Состояния результата
- start-limit-hit: служба заблокирована после серии перезапусковСостояние start-limit-hit и сообщение start request repeated too quickly: systemd перестал перезапускать службу. Как разблокировать и найти исходную причину.
- Failed with result 'exit-code'Состояние exit-code: служба завершилась с ненулевым кодом. Что это сообщение значит и где искать настоящую причину.
Конфигурация unit
- Unit is masked: служба запрещена к запускуСообщение Unit is masked: unit заблокирован ссылкой на /dev/null. Как найти и снять маскировку.
- Две службы конфликтуют: Conflicts= и взаимная остановкаЗапуск одной службы останавливает другую: параметр Conflicts=. Когда это полезно и когда мешает.
- Пределы ресурсов в unit-файле не применяютсяПочему limits.conf не действует на службы и как проверить действующие пределы процесса.
Сигналы
- signal=HUP (status=1/HUP) в systemdПроцесс службы завершён сигналом HUP. Обычно это перезагрузка настроек, которую программа поняла как команду выйти.
- signal=INT и signal=QUIT в systemdПроцесс службы завершён сигналом INT или QUIT. Кто их посылает службам и почему это обычно не systemd.
- signal=SEGV (status=11/SEGV) в systemdПроцесс службы завершён сигналом SEGV: обращение к недопустимой памяти. Как собрать дамп и что смотреть.
- signal=TERM (status=15/TERM) в systemdПроцесс службы завершён сигналом TERM. Когда это нормальная остановка, а когда признак проблемы.
Ошибки служб
- Apache: could not bind to addressApache не занимает порт: конфликт с nginx, порт занят, нет прав на привилегированный порт.
- Gunicorn: WORKER TIMEOUT в журнале службыРабочие процессы Gunicorn убиваются по таймауту: медленные запросы, блокирующие вызовы, неверный тип обработчика.
- Node-служба не может занять порт 80Приложение на Node падает при привязке к привилегированному порту: как дать возможность вместо запуска от root.
- certbot: проверка владения доменом не проходитОбновление сертификата не удаётся: занят порт 80, недоступен каталог проверки, перехвачен редиректом.
- nginx 403 Forbidden и Permission denied на файлы сайтаnginx отдаёт 403: нет прав на файлы сайта, закрыт каталог по пути, мешает SELinux или AppArmor, нет индексного файла.
- nginx 413 Request Entity Too LargeЗагрузка файла отклонена: превышен client_max_body_size. Где менять и что ещё ограничивает размер.
- nginx 502 Bad Gateway: connect() failed к приложениюnginx работает, а приложение недоступно: connect() failed, connection refused, no such file or directory для сокета. Разбор 502.
- nginx 504 Gateway Time-out: upstream timed outnginx не дождался ответа приложения. Разбор таймаутов proxy_read_timeout и fastcgi_read_timeout, поиск медленных мест.
- nginx reload не применяет измененияПерезагрузка nginx прошла, а изменения не действуют: правка не в том файле, файл не включён, кеш, старые рабочие процессы.
- nginx: SSL_do_handshake() failed и ошибки TLSОшибки рукопожатия TLS в журнале nginx: несовпадение версий протокола, отсутствие поддерживаемых наборов шифров, обращение по http к https-порту.
- nginx: [emerg] duplicate listen options и conflicting server namenginx не запускается из-за повторных объявлений: duplicate listen options, conflicting server name, duplicate default server.
- nginx: [emerg] host not found in upstreamnginx не запускается: не разрешается имя узла из upstream или proxy_pass. Разбор порядка запуска и работы с именами.
- nginx: [emerg] open() failed при чтении конфигурацииnginx не может открыть файл конфигурации, включённый через include, или файл сертификата. Разбор путей и прав.
- nginx: [emerg] unknown directivenginx не запускается: неизвестная директива в конфигурации. Опечатка, не тот контекст или отсутствующий модуль.
- nginx: could not build server_names_hashnginx не запускается при большом числе или длинных именах серверов: нужно увеличить server_names_hash_bucket_size.
- nginx: worker process exited on signalРабочий процесс nginx падает: сторонний модуль, нехватка памяти, ошибка в обработке запроса.
- nginx: worker_connections are not enoughnginx под нагрузкой: не хватает соединений на рабочий процесс. Как считать worker_connections вместе с LimitNOFILE.
- nginx: ошибки сертификата при запуске (SSL_CTX_use_PrivateKey)nginx не запускается из-за сертификата: не совпадает ключ, неполная цепочка, файл повреждён, нет прав на чтение ключа.
- php-fpm отдаёт старый код после выкладкиПосле обновления файлов сайт показывает прежнюю версию: кеш скомпилированного кода не сброшен.
- php-fpm: server reached pm.max_children settingПул php-fpm исчерпал рабочие процессы: сайт отвечает медленно или отдаёт 502. Как считать pm.max_children.
- uWSGI: веб-сервер не может писать в сокетnginx получает отказ доступа к сокету uWSGI: права и владелец сокета задаются в описании приложения.
Коды выхода этой службы
Числа в status=N ниже 200 назначает сама программа, 200 и выше — systemd, когда не смог подготовить запуск.
| status | Что означает у этой службы | Куда смотреть |
|---|---|---|
1/FAILURE |
самый частый: проверка конфигурации не прошла или порт занят. Точную причину даёт строка nginx: [emerg] ... выше. |
разбор |
203/EXEC |
путь к nginx в unit-файле неверен — бывает после ручной сборки в /usr/local. | разбор |
0/SUCCESS при inactive |
nginx ушёл в фон при Type=simple: нужен либо forking, либо daemon off. |
разбор |
Диагностика
Проверка конфигурации с указанием файла и строки. При отказе запуска начинать надо с неё.
sudo nginx -tПоказывает, упала предварительная проверка или главный процесс.
systemctl status nginx --no-pager -lСообщения nginx, которые не успели попасть в его собственный файл ошибок.
journalctl -u nginx -n 50 --no-pagerКто занимает веб-порты: вторая по частоте причина после ошибки в конфигурации.
sudo ss -tlnp | grep -E ":(80|443)\s"Печатает итоговую конфигурацию со всеми включёнными файлами: видно повторные объявления адресов.
sudo nginx -T | grep -n "listen\|server_name" | head -20Параметры unit, которые тут важны
- Type=forking для обычной сборки, simple при `daemon off`
- PIDFile=обязателен при forking, иначе systemd следит не за тем процессом
- ExecReload=перезагрузка настроек сигналом HUP без разрыва соединений
- LimitNOFILE=под нагрузкой значение по умолчанию быстро исчерпывается
- ExecStartPre=проверка конфигурации до запуска — именно её код вы видите при отказе
Частые вопросы
Почему systemctl status показывает «Control process exited», а не ошибку nginx?
Потому что упала предварительная команда проверки конфигурации. Она выполняется до главного процесса, и её код становится кодом службы. Читайте строки nginx выше — там причина с номером файла и строки.
Изменил конфигурацию, сделал reload — ничего не изменилось. Почему?
Скорее всего правка в файле, который не включён в конфигурацию. Команда nginx -T печатает итоговый вариант со всеми include: если вашей правки там нет, файл не подключён.
Источники
- Официальная документация nginx: командная строка и сигналы
- systemd.service(5)
- Воспроизведено на тестовой машине, systemd 255 (Ubuntu 24.04)