SystemdDoctor
служба не работает частое execstart пути права запуск

status=203/EXEC в systemd

Код 203/EXEC systemd выдаёт сам, когда добрался до вызова execve() и получил отказ. Это значит, что подготовка запуска прошла, а вот запустить указанную в ExecStart= программу не удалось. Сама программа при этом не выполнила ни одной своей инструкции, поэтому в журнале нет её сообщений — только строка от systemd.

Что это значит

Под кодом 203 скрывается ровно один системный вызов: execve(). systemd подготовил окружение процесса, сменил пользователя, применил ограничения, а затем попросил ядро заменить процесс указанной программой — и получил ошибку. Ядро отказывает по нескольким причинам, и рядом со строкой про EXEC systemd почти всегда печатает ту самую причину от ядра: No such file or directory, Permission denied, Exec format error.

Из-за этого разбор 203/EXEC всегда начинается не с кода, а с приписки к нему. Именно она отличает «файла нет» от «файл есть, но не исполняемый» и от «файл исполняемый, но в нём указан несуществующий интерпретатор».

На каком этапе. Ошибка возникает между подготовкой процесса и первой строкой самой программы. Всё, что настраивается в unit-файле (пользователь, каталог, окружение, ограничения), уже применено успешно.

Вероятные причины

По порядку: сверху то, что встречается чаще.

  1. В ExecStart= указан путь, которого нет

    Опечатка в пути, программа лежит в другом каталоге, каталог с ней смонтирован позже запуска службы или файл удалён при обновлении пакета. Ядро возвращает ENOENT, systemd переводит это в 203/EXEC.

  2. Путь относительный, а не абсолютный

    systemd не ищет программу в PATH и не имеет «текущего каталога» в привычном смысле. Запись вида ExecStart=myapp --serve или ExecStart=./server для него не имя команды, а несуществующий путь.

  3. У файла нет права на исполнение

    Файл на месте, но без бита x. Так бывает после распаковки архива, копирования через веб-интерфейс, переноса с файловой системы без прав и после git checkout в системах, где права не сохраняются.

  4. В строке ExecStart= использован синтаксис оболочки

    systemd запускает программу напрямую, без sh. Конвейеры, перенаправления, &&, подстановки в обратных кавычках и звёздочки в именах файлов не раскрываются: они уходят программе как обычные аргументы, а если с них начинается строка — путь оказывается бессмысленным.

  5. Скрипт ссылается на интерпретатор, которого нет

    При запуске скрипта ядро читает первую строку с #! и выполняет указанный там интерпретатор. Если путь в ней неверный — например #!/usr/bin/python на системе, где есть только python3, — ошибка ENOENT относится к интерпретатору, а не к скрипту. Выглядит это как «файл есть, но systemd говорит, что его нет».

  6. Файл не той архитектуры или повреждён

    Ядро отказывает с Exec format error, когда файл не является исполняемым для этой платформы: собран под другую архитектуру, скачан не до конца, оказался текстом без строки #! или это библиотека, а не программа.

Диагностика

Команды идут в том порядке, в котором их стоит выполнять: каждая следующая проверяет то, что осталось после предыдущей.

Показывает строку Process: с полным значением ExecStart и кодом. В той же выдаче видно, дошло ли дело до главного процесса или упало на ExecStartPre.

systemctl status myapp.service -l --no-pager

Рядом со строкой про шаг EXEC systemd печатает причину от ядра. Именно она определяет, какая из причин ниже ваша.

journalctl -xeu myapp.service --no-pager -n 50

Показывает unit-файл в том виде, в котором его применил systemd, вместе со всеми drop-in из каталогов *.d. Правка, забытая без daemon-reload, сразу видна.

systemctl cat myapp.service

Запуск той же программы от того же пользователя вручную. Если так она тоже не запускается, дело не в systemd, и разбираться надо с файлом и правами.

sudo -u app /opt/myapp/bin/server --version

Решение

Решение своё для каждой причины. Сначала определите, какая из них ваша, — иначе правки наложатся друг на друга.

1. В ExecStart= указан путь, которого нет
Почему происходит
Опечатка в пути, программа лежит в другом каталоге, каталог с ней смонтирован позже запуска службы или файл удалён при обновлении пакета. Ядро возвращает ENOENT, systemd переводит это в 203/EXEC.
Как проверить
Сравните путь из действующего unit-файла с тем, что есть на диске. Важно смотреть именно на действующий файл: правка в /etc/systemd/system могла не примениться без перезагрузки конфигурации.
systemctl cat myapp.service | grep ExecStart
ls -l /opt/myapp/bin/server
Как исправить
Поставьте правильный абсолютный путь. Точный путь к программе из системных каталогов показывает command -v. После правки unit-файла обязательно systemctl daemon-reload.
command -v myapp
sudo systemctl daemon-reload && sudo systemctl restart myapp.service
2. Путь относительный, а не абсолютный
Почему происходит
systemd не ищет программу в PATH и не имеет «текущего каталога» в привычном смысле. Запись вида ExecStart=myapp --serve или ExecStart=./server для него не имя команды, а несуществующий путь.
Как проверить
Проверьте, начинается ли значение ExecStart= с косой черты. Проверка unit-файла на этом сайте отмечает такие строки как ошибку.
systemctl cat myapp.service | grep -E "^Exec"
Как исправить
Замените имя команды полным путём. Если программа установлена в нестандартный каталог, путь всё равно пишется целиком — переменную PATH из вашего профиля служба не видит.
ExecStart=/usr/local/bin/myapp --serve
3. У файла нет права на исполнение
Почему происходит
Файл на месте, но без бита x. Так бывает после распаковки архива, копирования через веб-интерфейс, переноса с файловой системы без прав и после git checkout в системах, где права не сохраняются.
Как проверить
Посмотрите права: в выводе ls -l у исполняемого файла должны быть x для владельца и для тех, от чьего имени запускается служба.
ls -l /opt/myapp/bin/server
sudo -u app test -x /opt/myapp/bin/server && echo исполняемый || echo нет права
Как исправить
Добавьте право на исполнение. Если служба работает от отдельного пользователя, проверьте и права на все каталоги по пути — на каждый нужен бит x, иначе до файла просто не дойти.
sudo chmod 755 /opt/myapp/bin/server
4. В строке ExecStart= использован синтаксис оболочки
Почему происходит
systemd запускает программу напрямую, без sh. Конвейеры, перенаправления, &&, подстановки в обратных кавычках и звёздочки в именах файлов не раскрываются: они уходят программе как обычные аргументы, а если с них начинается строка — путь оказывается бессмысленным.
Как проверить
Поищите в строке запуска символы |, >, &&, ;, $(. Их наличие почти всегда означает, что unit писали как строку в терминале.
systemctl cat myapp.service | grep -nE "[|>&;]|\$\("
Как исправить
Вызовите оболочку явно и передайте ей всю команду одной строкой, либо, что надёжнее, перенесите логику в отдельный скрипт и запускайте его.
ExecStart=/bin/sh -c '/opt/myapp/bin/server >> /var/log/myapp.log 2>&1'
5. Скрипт ссылается на интерпретатор, которого нет
Почему происходит
При запуске скрипта ядро читает первую строку с #! и выполняет указанный там интерпретатор. Если путь в ней неверный — например #!/usr/bin/python на системе, где есть только python3, — ошибка ENOENT относится к интерпретатору, а не к скрипту. Выглядит это как «файл есть, но systemd говорит, что его нет».
Как проверить
Посмотрите первую строку скрипта и проверьте существование указанного в ней пути.
head -1 /opt/myapp/run.py
ls -l /usr/bin/python
Как исправить
Исправьте строку #! на существующий путь или запускайте интерпретатор явно, указав его в ExecStart= первым аргументом.
ExecStart=/usr/bin/python3 /opt/myapp/run.py
6. Файл не той архитектуры или повреждён
Почему происходит
Ядро отказывает с Exec format error, когда файл не является исполняемым для этой платформы: собран под другую архитектуру, скачан не до конца, оказался текстом без строки #! или это библиотека, а не программа.
Как проверить
Определите тип файла и сверьте архитектуру с машиной.
file /opt/myapp/bin/server
uname -m
Как исправить
Поставьте сборку под нужную архитектуру. Для скриптов добавьте первой строкой #!/bin/sh или #!/usr/bin/env python3.

Пример вывода

Служба с несуществующим путём в ExecStart=. Пример показательный: он собран на тестовой машине специально для этой страницы, а не взят из чужого журнала.

× myapp.service - My application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Mon 2026-09-14 09:12:41 MSK; 3s ago
    Process: 4118 ExecStart=/opt/myapp/bin/server --config /etc/myapp.yml (code=exited, status=203/EXEC)
   Main PID: 4118 (code=exited, status=203/EXEC)
        CPU: 1ms

systemd[4118]: myapp.service: Failed to locate executable /opt/myapp/bin/server: No such file or directory
systemd[4118]: myapp.service: Failed at step EXEC spawning /opt/myapp/bin/server: No such file or directory
systemd[1]: myapp.service: Main process exited, code=exited, status=203/EXEC
systemd[1]: myapp.service: Failed with result 'exit-code'.

Ключевая строка тут не про 203, а про No such file or directory. При запрете доступа на том же месте было бы Permission denied, а при неверной сборке — Exec format error.

Частые вопросы

Почему 203/EXEC, если файл точно на месте и исполняемый?

Чаще всего дело в строке #! внутри скрипта: путь к интерпретатору в ней неверный, и ядро жалуется на него, а не на сам скрипт. Вторая частая причина — запрет доступа не к файлу, а к одному из каталогов по пути: на каждый каталог нужен бит x.

Можно ли по коду понять, что именно случилось?

Нет, 203 сообщает только про отказ execve(). Причину даёт приписка в журнале: No such file or directory, Permission denied, Exec format error. Без неё разбирать бессмысленно.

Почему в терминале команда работает, а служба падает?

В терминале действует ваш PATH, текущий каталог и ваше окружение. У службы нет ничего из этого: путь только абсолютный, окружение задаётся в unit-файле, а пользователь может быть другим.

Связанные ошибки

  • status=200/CHDIR в systemd Код 200/CHDIR означает, что systemd не смог перейти в каталог из WorkingDirectory= до запуска программы. Причины и решение.
  • status=217/USER в systemd Код 217/USER означает, что systemd не смог определить или сменить пользователя из User=. Разбор причин: пользователя нет, имя недопустимо, конфликт с DynamicUser.
  • Exec format error при запуске службы Ошибка формата исполняемого файла: не та архитектура, нет строки #!, повреждённый файл или попытка запустить не программу.
  • Permission denied в журнале службы Отказ в доступе у службы systemd: права на файл, каталоги по пути, пользователь службы, параметры изоляции, SELinux и AppArmor.
  • No such file or directory в журнале службы Служба не находит файл или каталог. Разбор: путь, момент запуска, изоляция unit-файла, символические ссылки, приватный /tmp.
  • status=126 в systemd: файл не исполняется Код 126: команда найдена, но выполнить её нельзя. Отличие от 127 и от 203/EXEC, разбор причин.
  • Address already in use при запуске службы Порт или адрес уже занят: bind() failed (98: Address already in use). Как найти владельца порта и что делать с остатками прежнего процесса.
  • Job for … failed because a timeout was exceeded Задание на запуск прервано по таймауту. Как отличить медленный старт от заблокированного и правильно настроить TimeoutStartSec.

Где встречается чаще всего

Источники

  • systemd.exec(5)
    Таблица кодов выхода: 203 EXIT_EXEC — отказ системного вызова execve().
    официальная документация, systemd 255
    сверено 15 сентября 2026
  • systemd.service(5)
    Требование абсолютного пути в Exec*= и правила разбора строки запуска.
    официальная документация, systemd 255
    сверено 15 сентября 2026
  • Воспроизведено на тестовой машине, systemd 255 (Ubuntu 24.04)
    Проверено на сломанном unit: несуществующий путь, файл без бита x, скрипт с неверной строкой #!. В трёх случаях код один, приписка от ядра разная.
    собственная проверка, systemd 255
    сверено 15 сентября 2026