systemd 单元里那些容易踩的坑

2026-09-23 · 运维

Type=notify 在等什么

Type=notify 要求进程自己往 $NOTIFY_SOCKET 发 READY=1。进程不发的后果不是 立刻失败,而是卡在 activating 直到 TimeoutStartSec 超时,然后被判定为 Failed with result 'timeout'——期间端口可能已经监听了,服务看起来“又活着又死了”。

排查顺序:先看 systemctl status 的 Active 状态,再看 ss -tlnp 是否在听。 两者矛盾,基本就是 notify 没发。把 Type 改成 simple 通常立刻正常。

Restart= 的语义

Restart=no            # 不重启
Restart=on-failure    # 非 0 退出、被信号杀、超时 —— 会重启
Restart=always        # 除 systemctl stop 之外都重启

配合 RestartSec=5 是常规写法。但要留意 StartLimitIntervalSec 与 StartLimitBurst:默认 10 秒内失败 5 次就会进入 failed 且不再重启, 日志里那句 start request repeated too quickly 指的就是这个。

沙箱选项会悄悄改行为

ProtectSystem=strict 让整个文件系统只读,只剩 /dev、/proc、/sys; PrivateTmp=true 给进程一个独立的 /tmp。这两个加上去之后,程序写临时文件、 建锁文件都可能失败,而报错信息往往只说 “permission denied”。

建议做法:加沙箱之前先用 strace -f -e trace=openat 跟一遍,看它到底碰了哪些路径, 再决定用 ReadWritePaths= 开哪几个口子。比事后猜日志快得多。

最后一点

systemctl daemon-reload 只在你改了 unit 文件本身时才需要;改的是 /etc/default/ 里的环境变量就不用。这条搞混过好几次,记一下。