Ошибка code=exited, status=203/EXEC появляется в выводе systemctl status, когда systemd не смог запустить исполняемый файл, указанный в директиве ExecStart юнит-файла. Код 203 означает, что процесс даже не начал выполняться: ядро отказало на этапе вызова execve(), поэтому сама программа здесь ни при чём — искать нужно в путях, правах и окружении службы.
Типичный симптом выглядит так: служба падает сразу после systemctl start, в статусе видно Failed at step EXEC spawning /usr/bin/myapp: No such file or directory или Permission denied. Ниже разберём, как диагностировать причину и устранить её без риска для системы.
Что означает код 203/EXEC
В systemd каждому коду завершения соответствует своё значение. status=203/EXEC — это сигнал о том, что systemd не смог выполнить системный вызов запуска бинарного файла. Процесс форкнулся, но загрузить программу в память не удалось, и служба завершилась ещё до исполнения первой строки кода.
Ключевая подсказка всегда находится в строке журнала рядом с кодом. Там указывается конкретная причина отказа: отсутствующий файл, недостаток прав, неверный интерпретатор скрипта или ограничения безопасности. Именно эту строку нужно читать первой, а не сам код 203.
Основные причины ошибки
Чаще всего отказ на этапе EXEC вызван одной из нескольких типовых причин. Проверять их стоит в том порядке, в котором они перечислены ниже — от самых частых к более редким.
- 📂 Неверный путь к исполняемому файлу — опечатка в
ExecStartили программа установлена в другой каталог, чем указано в юните. - 🔒 Отсутствие права на исполнение — у файла не установлен бит
+x, либо пользователь службы не имеет доступа к каталогу. - 📜 Битый shebang в скрипте — первая строка вида
#!/bin/bashуказывает на несуществующий интерпретатор или содержит символы Windows-переноса строк (CRLF). - 🛡️ Ограничения безопасности — SELinux, AppArmor или директивы юнита вроде
ProtectSystemиNoNewPrivilegesблокируют запуск. - 🏗️ Несовместимая архитектура — бинарник собран под другую платформу, например ARM-файл пытаются запустить на x86_64.
⚠️ Внимание: сообщение No such file or directory не всегда означает, что файла нет. Оно же появляется, когда отсутствует интерпретатор из shebang-строки скрипта или динамический загрузчик бинарника.
Диагностика: как найти точную причину
Первым делом посмотрите полный статус службы и свежие записи журнала. Команда systemctl status имя_службы покажет строку с причиной отказа, а более подробный вывод даст журнал:
journalctl -u имя_службы -n 50 --no-pager
Далее проверьте, существует ли файл из ExecStart и можно ли его запустить. Выполните ls -l /путь/к/файлу и убедитесь, что в правах присутствует x. Затем попробуйте запустить файл вручную от имени пользователя, под которым работает служба:
sudo -u имя_пользователя /путь/к/файлу
Если при ручном запуске программа стартует нормально, а через systemd падает с 203/EXEC, проблема почти наверняка в окружении юнита: переменных, рабочем каталоге или ограничениях безопасности. Если не запускается и вручную — смотрите на сам файл: его формат, shebang и зависимости.
Пошаговое исправление
Ниже — безопасная последовательность действий, которая не требует пересборки пакетов и не затрагивает системные файлы за пределами вашего юнита.
- ✅ Откройте юнит-файл командой
systemctl cat имя_службыи сверьте путь вExecStartс реальным расположением программы. - ✅ Установите право на исполнение:
chmod +x /путь/к/файлу. - ✅ Проверьте владельца и права на все родительские каталоги — пользователь службы должен иметь туда доступ на чтение и вход.
- ✅ Для скриптов проверьте первую строку и пересохраните файл в формате Unix (LF), например через
dos2unixили настройки редактора. - ✅ После любых правок юнита выполните
systemctl daemon-reloadи только потом перезапускайте службу.
☑️ Проверка перед перезапуском службы
Если служба использует EnvironmentFile или переменные в ExecStart, убедитесь, что файл окружения существует и читается. Отсутствующий EnvironmentFile без знака - перед путём тоже может приводить к сбою запуска.
Типовые сценарии и их решения
Разные сообщения в журнале при одном и том же коде 203 требуют разных действий. Сводная таблица поможет быстро сопоставить симптом и решение.
| Сообщение в журнале | Вероятная причина | Что делать |
|---|---|---|
| No such file or directory | Неверный путь или отсутствует интерпретатор | Проверить путь и shebang скрипта |
| Permission denied | Нет права на исполнение или доступ к каталогу | chmod +x, проверить владельца и SELinux |
| Exec format error | Битый файл или чужая архитектура | Проверить file, переустановить пакет |
| Failed at step EXEC с ProtectSystem | Ограничения sandbox-директив юнита | Скорректировать директивы безопасности |
⚠️ Внимание: ослабляя директивы безопасности вродеProtectSystem,PrivateTmpилиNoNewPrivileges, делайте это точечно и понимайте, зачем они были включены. Полное их отключение ради «заработало» снижает защищённость системы.
Особые случаи: SELinux, контейнеры, обновления
На системах с SELinux (RHEL, CentOS, Fedora) файл может существовать и иметь все права, но запуск блокируется из-за неверного контекста безопасности. Проверить это можно командой ls -Z /путь/к/файлу и поиском отказов в ausearch -m avc -ts recent, если утилита audit установлена. Восстановить контекст для стандартных путей помогает restorecon -v /путь/к/файлу.
Отдельная ситуация — ошибка появилась после обновления пакета, который изменил расположение бинарника, а юнит-файл остался со старым путём. Такое бывает с кастомными юнитами, созданными вручную поверх пакетных. Решение — сверить ExecStart с актуальным путём из свежей версии пакета, например через which или whereis.
Почему скрипт с CRLF даёт ошибку 203
Если скрипт создан в Windows, строки завершаются символами \r\n. Ядро воспринимает shebang как #!/bin/bash\r — то есть ищет интерпретатор с невидимым символом возврата каретки в имени, не находит его и возвращает No such file or directory. Лечится пересохранением файла в формате LF.
Как предотвратить повторение ошибки
Большинство случаев 203/EXEC связано с ручным созданием юнитов и скриптов. Несколько привычек заметно снижают риск: всегда проверяйте новый юнит командой systemd-analyze verify /путь/к/юниту, которая укажет на синтаксические проблемы, и тестируйте запуск бинарника вручную от целевого пользователя до включения службы.
Для скриптов держите shebang корректным, сохраняйте файлы в Unix-формате и не кладите исполняемые файлы в каталоги, смонтированные с опцией noexec — проверить это можно в выводе mount | grep /точка/монтирования. После любых правок юнитов не забывайте про daemon-reload, иначе systemd продолжит использовать старую версию конфигурации.
Частые вопросы
Чем код 203/EXEC отличается от 203/EXIT?
203/EXEC означает, что программа не смогла запуститься на этапе системного вызова exec — файл не найден, нет прав или неверный формат. Код 203/EXIT встречается редко и означает, что программа запустилась, но завершилась с кодом возврата 203, который ей вернуло само приложение.
Файл существует и запускается вручную, но служба падает с 203 — почему?
Скорее всего, дело в окружении: служба работает от другого пользователя, у которого нет доступа к файлу или каталогам, либо запуск блокируют SELinux/AppArmor или sandbox-директивы юнита. Проверьте запуск через sudo -u пользователь_службы и журнал аудита безопасности.
Нужно ли делать daemon-reload после правки юнита?
Да. systemd кэширует содержимое юнит-файлов, и без systemctl daemon-reload служба будет запускаться по старой конфигурации. Это частая причина ситуации «я всё исправил, а ошибка осталась».
Может ли ошибка 203 появиться из-за docker или контейнера?
Да, если юнит запускает контейнер, а бинарник внутри образа отсутствует, не исполняемый или собран под другую архитектуру. В этом случае проверяйте команду запуска контейнера и точку входа образа, а также совпадение архитектуры образа и хоста.
Как понять, что виноват именно SELinux, а не права?
Если ls -l показывает корректные права, но запуск под systemd всё равно отклоняется, посмотрите контекст файла через ls -Z и поищите записи отказов (denied) в журнале аудита. Временный перевод в режим permissive помогает подтвердить диагноз, но постоянным решением должен быть правильный контекст файла, а не отключение защиты.