第 14 期 2026 年 8 月 广东

搜索
我的生活点滴

技术笔记与学习备忘

2026-08-04 工程实践 约 12 分钟 陈予安

把 systemd 服务写明白:从 unit 文件到失败排查

服务起不来时,我现在先看 Type、User 和 journal,而不是先把 ExecStart 改得很长。下面是一份能直接改的模板,以及我反复踩过的几处。

代码编辑器里打开的配置文件

先选对 Type

大多数自己写的 HTTP 服务用 Type=simple 就够:进程在前台跑,systemd 认为主进程退出即服务退出。只有程序会自己 fork 到后台时,才考虑 forking,并且一定要配 PIDFile=

如果进程启动后还要等端口就绪,用 Type=notify 并在程序里发 READY=1。不要用 sleep 假装就绪,重启时序一变就会踩空。

一份常用模板

我把长期跑的 Python 服务放在 /opt/notes-api,用独立用户,不给 login shell。unit 大致如下:

[Unit]
Description=notes api
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=notes
Group=notes
WorkingDirectory=/opt/notes-api
EnvironmentFile=/etc/notes-api.env
ExecStart=/opt/notes-api/.venv/bin/python -m app
Restart=on-failure
RestartSec=3
NoNewPrivileges=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

EnvironmentFile 比在 unit 里堆 Environment= 好改。文件权限给 0640,属主 root,组给运行用户。

失败时看 journal

systemctl status notes-api,再 journalctl -u notes-api -n 80 --no-pager。status 只能告诉你退出码,真正的 ImportError、权限错误、地址占用都在 journal 里。

常见三类:

把 ExecStart 写成一长串环境变量,是我以前最差的习惯。变量一多,报错位置就看不见了。

Restart 不要写太猛

Restart=always 会把配置错误也当成可恢复故障。生产里我用 on-failure,并加 StartLimitBurst=5StartLimitIntervalSec=60,避免配置写错时打满磁盘。

改完 unit 记得 systemctl daemon-reload。只 restart 不会重新读文件,这点我每年至少忘一次。

参考:systemd.service(5)