首页 / 内容指南 / 当前文章

systemd服务报status=203/EXEC怎么办:路径、权限与解释器排查

发布于 2026-08-26 · Content Fleet 编辑部

先运行 systemctl status 服务名 --no-pager -l 和 journalctl -u 服务名 -b --no-pager,确认日志里同时出现 status=203/EXEC、Failed at step EXEC 或 Permission denied/No such file or directory。再用 systemctl cat 与 systemctl show -p FragmentPath -p DropInPaths -p ExecStart 查看 systemd 实际加载的配置,而不是只看手边的 unit 文件。对最终 ExecStart 绝对路径执行 namei -l、stat、file,检查每一级目录、执行位和脚本 shebang。最后以 unit 的 User= 身份运行同一命令,并检查 noexec 挂载、安全策略与动态链接器。

直接答案

先运行 systemctl status 服务名 --no-pager -ljournalctl -u 服务名 -b --no-pager,确认日志里同时出现 status=203/EXECFailed at step EXECPermission denied/No such file or directory。再用 systemctl catsystemctl show -p FragmentPath -p DropInPaths -p ExecStart 查看 systemd 实际加载的配置,而不是只看手边的 unit 文件。对最终 ExecStart 绝对路径执行 namei -lstatfile,检查每一级目录、执行位和脚本 shebang。最后以 unit 的 User= 身份运行同一命令,并检查 noexec 挂载、安全策略与动态链接器。

一、确认失败发生在EXEC阶段

应用自己的退出码通常说明进程已经被成功创建;203 则属于 systemd 的执行阶段错误。先保存完整状态和本次启动日志:

systemctl status example.service --no-pager -l
journalctl -u example.service -b --no-pager -n 200
systemctl show example.service -p Result -p ExecMainCode -p ExecMainStatus

不要只截取最后一行。日志中的 No such file or directory 有时并非目标脚本不存在,而是脚本首行指定的解释器不存在;Permission denied 也可能来自上级目录、noexec 或强制访问控制,而不只是文件缺少 x 位。

二、读取systemd真正生效的配置

管理员可能修改了 /etc/systemd/system,软件包在 /usr/lib/systemd/system 提供了另一份 unit,或者 drop-in 又覆盖了 ExecStart=。用以下命令取得权威配置:

systemctl cat example.service
systemctl show example.service -p FragmentPath -p DropInPaths -p ExecStart -p User -p Group
systemd-analyze verify /etc/systemd/system/example.service

若刚编辑 unit,先运行 systemctl daemon-reload,再重新读取 systemctl show。修改了文件却未 reload 时,磁盘内容与管理器内存中的配置不同,反复改权限也不会解决旧路径问题。systemd-analyze verify 可发现未知指令、语法错误和部分不可执行命令,但不能替代运行身份与安全策略检查。

三、ExecStart使用明确的可执行路径

ExecStart= 的第一个参数应是可执行文件的绝对路径,或位于 systemd 固定搜索路径中的简单名称。不要依赖交互式 shell 的 alias、函数、当前目录或用户自定义 PATH。例如将:

ExecStart=python app.py

改为可验证的形式:

WorkingDirectory=/srv/example
ExecStart=/srv/example/.venv/bin/python /srv/example/app.py

先分别执行 stat /srv/example/.venv/bin/pythonreadlink -f,排除损坏软链接。若路径来自自动部署,检查新 release 是否真实存在以及 current 符号链接是否原子切换到了完整目录。

四、检查文件与每一级目录权限

仅执行 chmod +x 程序 不够。服务用户还需要对路径上的每一级目录拥有搜索权限。使用:

namei -l /srv/example/bin/server
stat -c '%A %U:%G %n' /srv /srv/example /srv/example/bin /srv/example/bin/server

如果 unit 设置 User=app,用同一身份验证:

sudo -u app test -x /srv/example/bin/server && echo executable
sudo -u app /srv/example/bin/server --version

不要为了快速通过而把整棵目录设为 777。应修正属主、组和最小必要权限;包含密钥或配置的文件不应因为执行故障被放宽为所有人可读。

五、脚本必须有有效的shebang

直接把脚本写进 ExecStart= 时,内核需要从首行找到解释器,例如:

#!/usr/bin/env python3

或更确定的虚拟环境路径:

#!/srv/example/.venv/bin/python

运行 head -n 1 脚本file 脚本sed -n '1l' 脚本,检查解释器路径、Windows CRLF 和 UTF-8 BOM。#!/bin/bash^M 会让内核寻找一个带回车字符的文件名,从而显示看似矛盾的“文件不存在”。生产 unit 更推荐直接执行解释器并把脚本作为参数,以便路径和版本清晰可审计。

六、不要默认systemd会启动Shell

systemd 不会像交互式终端那样自动解释管道、重定向、变量展开和 &&。下面的写法不会按 shell 语义执行:

ExecStart=/usr/bin/program > /var/log/program.log

优先使用 StandardOutput=StandardError= 和多个原生命令选项。确实需要 shell 时显式写:

ExecStart=/bin/sh -c 'exec /usr/bin/program --flag'

同时注意转义与环境变量来源。不要把不可信输入拼进 sh -c,否则排障修复会引入命令注入风险。

七、核对User、Group与环境

手工以 root 执行成功,不代表 unit 能成功。读取 User=Group=SupplementaryGroups=WorkingDirectory=Environment=EnvironmentFile=,然后用相同用户和工作目录复现。服务账户可能没有登录 shell、HOME 不同、PATH 更短,或无法访问位于 /root、个人家目录和网络挂载中的程序。

不要依赖 .bashrc.profile 或终端中临时导出的变量。把必要环境放入受控的 EnvironmentFile=,或由凭据机制提供敏感值;验证文件权限允许服务读取。环境问题通常导致应用启动后退出,但若它改变了解释器或动态加载路径,也可能表现为 EXEC 阶段失败。

八、检查noexec挂载与文件系统状态

程序存在且权限正确时,运行:

findmnt -T /srv/example/bin/server -o TARGET,SOURCE,FSTYPE,OPTIONS
mount | grep noexec

临时目录、用户目录、容器卷或强化挂载可能带 noexec。正确修复是把可执行文件部署到允许执行的受控目录,或在安全评估后调整该挂载,而不是把脚本复制到随机路径。还要确认文件系统未只读损坏、网络挂载已就绪;必要时在 unit 中声明正确的 mount 依赖。

九、检查二进制格式、架构与动态加载器

对二进制执行:

file /srv/example/bin/server
readelf -l /srv/example/bin/server | grep interpreter
ldd /srv/example/bin/server
uname -m

错误架构、损坏文件或缺失 ELF interpreter 都可能让执行失败。ldd 显示普通共享库缺失时,程序有时会被成功启动后由加载器报错;若内核指定的动态加载器本身不存在,则更接近“文件明明存在却无法执行”。不要从未知来源随意复制 loader 或 libc,应使用目标发行版的软件包或为正确平台重新构建产物。

十、检查SELinux、AppArmor与systemd沙箱

传统权限全部正常后,检查内核审计日志和安全模块:

journalctl -k -b | grep -Ei 'denied|apparmor|avc'
ausearch -m AVC -ts recent

若 SELinux 拒绝执行,先检查文件上下文是否因复制部署而错误,使用发行版提供的策略与 restorecon 恢复预期标签。不要长期使用 setenforce 0 作为修复。AppArmor 则应核对对应 profile 与实际路径。

unit 中的 ProtectSystem=ProtectHome=InaccessiblePaths=RootDirectory=RootImage=BindPaths= 等也会改变进程看到的文件系统。运行 systemctl showsystemd-analyze security 检查沙箱配置;确认路径在服务命名空间内真实可见,再做最小范围调整。

十一、一个可靠的逐层排查顺序

先保存状态和日志;再读取生效 unit、drop-in、用户与最终 ExecStart;然后依次验证路径、软链接、每级目录权限、执行位、shebang、CRLF、相同用户执行、挂载选项、二进制架构和安全审计。每次只改变一个因素并立即重新测试。

修复后运行:

systemctl daemon-reload
systemctl reset-failed example.service
systemctl start example.service
systemctl status example.service --no-pager -l
journalctl -u example.service -b --no-pager -n 100

reset-failed 只清除失败计数与状态,不会修复 EXEC 根因。若服务启动后出现新的应用错误,那说明执行阶段已经通过,应转入应用配置、网络和依赖排查,而不是继续修改执行权限。

十二、修复后的验收

确认 systemctl is-active 返回 active,主进程 PID 对应预期二进制,运行用户和工作目录正确。执行一次正常重启和一次主机重启,验证挂载、网络与密钥依赖不会因启动顺序再次触发 203。若 unit 支持 reload,也单独验证 reload 不会调用失效路径。

最后把实际 unit、drop-in、程序哈希、包版本和修复原因记入变更记录。监控 Result=exit-code、203 状态和重启频率;部署流程在切换 release 前执行 systemd-analyze verify、路径可执行性检查与目标架构验证,避免同类问题再次上线。

常见问题 FAQ

文件存在,为什么仍显示No such file or directory?

常见原因是脚本 shebang 指向不存在的解释器、首行带 CRLF,或 ELF 动态加载器不存在。分别检查 headfilesed -n '1l'readelf -l

chmod 777能不能快速解决?

不应该。它无法修复错误路径、解释器、noexec、架构或安全策略,还会扩大权限。用 namei -l 找出真正缺少的目录搜索权或文件执行权,做最小修正。

为什么命令在终端能运行,在systemd里失败?

两者的用户、工作目录、PATH、环境变量、挂载命名空间和安全限制可能不同。用 systemctl show 读取实际上下文,再以服务用户执行绝对路径命令。

修改unit后必须daemon-reload吗?

必须。否则 systemd 管理器仍使用内存中的旧配置。reload 后再用 systemctl show -p ExecStart 确认新值已经加载。

203/EXEC和应用退出码1有什么区别?

203 表示程序通常没有成功进入执行状态;退出码 1 表示程序已启动并主动返回失败。前者查路径与执行环境,后者应阅读应用自己的错误日志。

总结

status=203/EXEC 的关键是把问题限定在“systemd 无法执行目标程序”。权威排查链路是:读取生效配置,确认绝对路径,验证每级目录与服务身份,检查 shebang 和文件格式,再检查 noexec、二进制加载器及安全策略。避免用 777、关闭 SELinux 或无限重启掩盖根因;让部署预检提前验证同一组条件,才能真正消除复发。

官方资料

  • systemd.exec:https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
  • systemd.service:https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html
  • systemd-analyze:https://www.freedesktop.org/software/systemd/man/latest/systemd-analyze.html
  • systemd.unit:https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html