应立即执行sudo systemctl start your-service.service && sudo journalctl -u your-service.service -f观察启动瞬间输出,若无任何日志则进程未完成初始化;再用journalctl -u your-service.service --all --no-pager查boot前被截断日志;重点核对Type=配置是否匹配实际行为(如simple下父进程退出导致误判就绪),并用systemd-analyze critical-chain和strace逐层定位依赖或系统调用卡点。

看 journalctl 实时日志,别等超时才动手
systemd 的 start job timed out 是个假象,它只说明“我等了 90 秒没等到就放弃了”,不代表服务真花了 90 秒。真正的问题往往在启动后头 1 秒内就卡死了,但日志可能一闪而过。
立刻执行:sudo systemctl start your-service.service && sudo journalctl -u your-service.service -f,盯着输出有没有 “Starting…”、进程 PID、或第一行 error。如果啥都没刷出来就退出,说明进程连初始化都没完成。
更彻底一点,加 --all 参数拉全量日志:journalctl -u your-service.service --all --no-pager,尤其要查 boot 前、未刷盘或被截断的早期记录——很多关键错误(比如配置文件语法错)只在首次加载时打印一次。
Type= 配置错会导致 systemd 误判就绪时机
systemd 根据 Type= 决定“什么时候算启动成功”。配错就会让 systemd 在错误时间点开始计时,结果必然超时。
-
Type=simple(默认):认为ExecStart启动的进程就是主进程,它一退出,systemd 就算启动完成。但 Python/Node.js 脚本常 fork 子进程后父进程立刻 exit → systemd 认为“已就绪”,实际子进程还在加载配置,90 秒后直接 timeout -
Type=forking:要求服务明确 fork,并把子 PID 写入PIDFile=。若没写、路径错、或权限不足导致写失败,systemd 找不到主进程,只能干等超时 -
Type=notify:服务必须调用sd_notify("READY=1")主动通知。没调用、调用太晚、或二进制不链接libsystemd,就永远等不到 READY
查当前配置:systemctl cat your-service.service | grep Type=,再对照服务实际行为判断是否匹配。
顺着依赖链找瓶颈,别只盯自己服务
你的服务本身可能启动飞快,但被上游卡住。systemd 是按依赖图串行调度的,network.target、remote-fs.target、cryptsetup.target 这些都是常见堵点。
运行:systemd-analyze critical-chain your-service.service,顶部显示最耗时的末端单元,底部是最先启动的前置依赖。如果某依赖显示 inactive 或 not-found,问题根本不在你这儿。
再查它依赖谁:systemctl list-dependencies --reverse your-service.service,逐个 systemctl status 看这些依赖的状态。特别留意那些带 @ 的实例化服务(如 nginx@default.service),漏掉 @ 就等于查错对象。
用 strace 直接抓卡点,绕过 systemd 封装
当 journalctl 没输出、Type 也对、依赖都正常,问题大概率藏在系统调用层:open 配置失败?connect 数据库超时?write 日志目录无权限?这些都不会进 journal,但 strace 能看见。
手动模拟启动并跟踪关键系统调用:sudo strace -f -e trace=execve,openat,connect,write -s 200 -o /tmp/svc.strace /usr/bin/your-binary args
重点看最后几行:
- 卡在
openat(AT_FDCWD, "/etc/your.conf", ...)→ 配置路径错或权限不够 - 卡在
connect(..., {sa_family=AF_INET, sin_port=htons(5432), ...})→ 数据库没起来或防火墙挡了 - 卡在
write(2, "Permission denied", ...)→ stderr 写不出,可能是ProtectSystem=strict挂载了/usr只读,连动态库都加载不了
超时类故障最难的不是定位,而是意识到“它根本没走到你想查的那步”——strace 能逼它暴露第一步就失败的真实原因。











