结论先放这里:systemd 服务「开机不自启、手动 start 却一切正常」,十有八九不是程序问题,而是 unit 文件里 [Install] 段没写、依赖顺序不对,或者 ExecStart 用了相对路径。按下面三步查,半小时内能定位。
第一步:确认 enable 真的生效了
先别急着怀疑系统,用两个命令看状态:
systemctl is-enabled myapp.service
systemctl status myapp.service
is-enabled 输出 enabled,说明安装段没问题;输出 disabled 或提示 has no installation config,就是 unit 文件缺 [Install] 段,补上再重新 enable:
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl reenable myapp.service
第二步:看开机时系统到底拉没拉它
enable 是对的还不起,问题多半出在启动时序。用 journalctl 看本次开机日志,注意 -b 参数只查这次启动:
journalctl -b -u myapp.service
一条日志都没有,说明开机时根本没拉起它,回到上一步查安装段和 target;有日志但启动失败,最常见的原因是依赖没就绪:服务要网络,unit 里就要写 After=network-online.target 和 Wants=network-online.target,光写 network.target 不够,那个 target 不等网络真正可用。加载状态也可以顺手确认一下:systemctl list-unit-files | grep myapp 输出 enabled 且没被 masked,才值得继续往下查时序。
嫌翻日志麻烦,可以直接看启动链路卡在哪:
systemd-analyze critical-chain myapp.service
→
→
→
第三步:环境差异,开机时 PATH 几乎是空的
手动启动时你有登录 shell,PATH、JAVA_HOME 全在;systemd 拉起服务时是一套精简环境,ExecStart=/usr/bin/python3 /opt/app/run.py 这种写法最稳,写成 ExecStart=python3 run.py 大概率报 command not found。同样,脚本里用到的命令也要写绝对路径,需要工作目录就显式写 WorkingDirectory=/opt/app,不写默认是 /,相对路径全部失效。
| 症状 | 最可能的原因 | 先做哪一步 |
|---|---|---|
| enable 时报 has no installation config | 缺 [Install] 段 | 补 WantedBy 后 reenable |
| 日志里报网络/数据库连不上 | 启动早于依赖就绪 | 加 After=network-online.target |
| 日志报 command not found | PATH 精简 | ExecStart 写绝对路径 |
踩坑实录
前阵子给客户配监控采集器,unit 文件明明 enabled,重启后服务就是不起来,手动 start 秒起。翻 journalctl -b -u exporter 一条日志都没有,我一度怀疑是 systemd 版本问题。最后用 systemd-analyze critical-chain exporter.service 一看,卡在 network-online.target 之前。采集器要连外网 API,unit 里只写了 After=network.target,而 DHCP 还没拿到 IP 服务就先起来了。加上 Wants=network-online.target 并 enable systemd-networkd-wait-online.service,重启验证通过。
常见问题
Q:改完 unit 文件要不要重启服务器?
不用。先 systemctl daemon-reload 再 systemctl restart 即可,但「开机不自启」这类时序问题,建议最后还是 reboot 一次真实验证,很多手动 restart 正常、重启才暴露的坑就是这么查出来的。
Q:为什么手动 start 正常,开机就失败?
手动启动时所有依赖(网络、磁盘、数据库)都已就绪,且环境变量齐全;开机时服务在依赖链里排队,早了网络没就绪,环境也精简,所以同样的命令两种场景结果不同。
Q:Restart=always 能解决吗?
不能。开机时服务压根没被拉起(inactive),Restart 只对「启动后崩溃退出」生效。先解决拉起时机,再考虑 Restart 兜底。
Q:同一个服务,为什么有的机器自启、有的不自启?
多半是 unit 文件来源不同。系统包自带的在 /usr/lib/systemd/system,手写覆盖的在 /etc/systemd/system,后者优先级更高。两台机器对比排查时,先把两边的 systemctl cat 服务名 输出对齐,差异通常一眼可见。
排查时如果遇到类似的 systemd 坑,欢迎在 fenij.com 评论区留言,说说你踩的是哪一种,我尽量把更多真实案例补进来。