根本原因是环境变量、工作目录或权限上下文不一致;systemd默认环境极简且不加载shell配置,需显式设置environment、workingdirectory等,并注意type、wantedby、daemon-reload等关键配置。

systemd 服务文件里 ExecStart 启动失败,但手动运行命令却正常
根本原因通常是环境变量、工作目录或权限上下文不一致。systemd 默认不加载用户 shell 的 ~/.bashrc 或 /etc/environment,PATH 也极简(通常只有 /usr/bin:/bin)。
- 用
systemctl show --property=Environment <service-name></service-name>查当前环境 - 在 service 文件中显式设置:
Environment="PATH=/usr/local/bin:/usr/bin:/bin" - 加
WorkingDirectory=<code>/path/to/app避免相对路径失效 - 如果依赖用户级密钥或 socket(比如
~/.gnupg),考虑改用User=root或配DynamicUser=yes+StateDirectory=
从 SysV init 迁移到 systemd 时,start-stop-daemon 脚本无法直接复用
systemd 不执行传统 init 脚本里的 fork/daemonize 逻辑,它自己管理进程生命周期。硬套 start-stop-daemon 极易导致状态错乱(比如 systemctl status 显示 inactive (dead),但进程还在跑)。
- 删掉所有
start-stop-daemon、pidfile相关逻辑 -
Type=选对:普通前台进程用Type=simple;需要等待就绪的用Type=notify(需程序支持 sd_notify);遗留 daemon 用Type=forking+ 正确PIDFile= - 别再写
/var/run/<service>.pid</service>,systemd 自动跟踪主进程 PID - 日志统一走
journald,删掉脚本里重定向到/var/log/xxx.log的逻辑(除非真有归档需求)
systemctl restart 不生效,或提示 Unit xxx.service not found
不是服务没装,而是 unit 文件没被 systemd 加载识别。常见于自定义 service 文件放错位置、未重载配置、或文件名不匹配。
- 确认文件路径:用户级服务放
~/.config/systemd/user/,系统级必须放/etc/systemd/system/(优先级高于/usr/lib/systemd/system/) - 文件名必须以
.service结尾,且和systemctl命令中用的名字完全一致(比如myapp.service,启动就得用systemctl start myapp,不能写myapp.service) - 改完文件后必须执行
systemctl daemon-reload,否则 systemd 完全无视变更 - 用
systemctl list-unit-files | grep myapp确认是否已识别;用systemctl cat myapp看实际加载的是哪个文件
为什么 systemctl enable 后开机不自动启动?
enable 只是创建软链接,真正启动取决于 unit 文件里的 WantedBy= 和目标(target)是否激活。最常踩的坑是服务依赖了未启用的 target,或写了 WantedBy=multi-user.target 却系统默认进了 graphical.target(如桌面环境)。
- 检查
systemctl get-default输出,确认默认 target 是你要的(比如服务器应为multi-user.target) - 确保 service 文件中有
WantedBy=multi-user.target(或对应 target),且没被ConditionPathExists=等条件意外屏蔽 - 用
systemctl is-enabled <service></service>确认返回enabled,不是disabled或static - 如果服务依赖网络,加
After=network-online.target和Wants=network-online.target,否则可能因网卡未就绪而跳过启动
真正麻烦的从来不是语法,而是那些没报错、但行为和预期差一拍的上下文细节——比如 journal 日志里没错误,只是进程默默退出了;或者 systemctl status 显示 active,但端口根本没监听。盯住 journalctl -u <service> -n 50 -f</service> 和 systemctl show <service></service>,比反复改配置更省时间。










