systemd服务文件必须指定workingdirectory和environment;workingdirectory设为项目根目录,environment至少包含path和pythonpath;type=simple适用于前台脚本,type=notify适用于gunicorn等支持通知的服务;restart=on-failure需配合restartsec和startlimitintervalsec防雪崩;应使用低权用户并配置readwritepaths确保日志等路径可写。

systemd服务文件必须指定WorkingDirectory和Environment
不设WorkingDirectory,Python程序里用相对路径读配置或写日志会失败;不显式声明Environment="PATH=/usr/local/bin:/usr/bin:/bin",pipenv、poetry或虚拟环境里的解释器可能根本找不到。常见错误现象是服务启动后立刻退出,journalctl -u myapp.service 显示 ModuleNotFoundError 或 No such file or directory。
实操建议:
-
WorkingDirectory必须设为项目根目录(如/opt/myapp),不能是空值或默认值 -
Environment至少包含PATH和虚拟环境变量,例如:Environment="PATH=/opt/myapp/venv/bin:/usr/local/bin:/usr/bin:/bin"Environment="PYTHONPATH=/opt/myapp/src" - 若用
venv,推荐直接调用绝对路径的解释器:ExecStart=/opt/myapp/venv/bin/python app.py,而非依赖PATH
Type=simple vs Type=notify:选错会导致systemd误判进程状态
Python 程序默认是前台阻塞运行,Type=simple 最稳妥;但如果你用了 gunicorn、uvicorn 或自己调用了 sd_notify(),就得配 Type=notify,否则 systemd 会等超时后强行杀掉进程,报错 Failed with result 'timeout'。
实操建议:
- 纯脚本(如
while True:循环)→ 用Type=simple - Web 服务(
uvicorn main:app --workers 2)→ 加--reload以外必须配Type=notify+NotifyAccess=all - 不确定是否支持 notify?先用
Type=simple,再看systemctl is-active myapp是否稳定为active (running)
Restart=on-failure 不等于“崩溃就重启”,得配合RestartSec和StartLimitIntervalSec
只写 Restart=on-failure,一旦 Python 进程因异常退出,systemd 可能在 1 秒内反复拉起又失败,触发默认限流(10 次/10 秒),之后直接停服并标记 failed。你看到 systemctl status myapp 显示 start-limit-hit 就是这个原因。
实操建议:
- 加
RestartSec=5控制重启间隔,避免雪崩 - 放宽限制:
StartLimitIntervalSec=600+StartLimitBurst=5,允许 10 分钟内最多重启 5 次 - 对调试阶段,可临时加
Restart=always,但上线前务必改回on-failure,防止掩盖真实错误
权限与用户隔离:别让服务以root跑,也别漏掉ReadWritePaths
用 User=www-data 或自建低权用户(如 User=myapp)是必须的;但只改用户不配文件权限,Python 写日志、缓存或上传文件时会报 Permission denied。systemd 默认禁止写 /tmp 以外的路径,除非显式声明。
实操建议:
- 创建专用用户:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp - 设
User=myapp+Group=myapp,并确保项目目录属主为该用户:sudo chown -R myapp:myapp /opt/myapp - 若需写日志到
/var/log/myapp/,加ReadWritePaths=/var/log/myapp,否则会被ProtectSystem=full拦住 - 禁用不需要的功能可加
NoNewPrivileges=true和PrivateTmp=true,但要确认程序没硬编码依赖/tmp
ProtectHome=true 和 ProtectSystem=strict 的副作用——它们会让 Python 读不到 ~/.config 或写不了 /etc/myapp/conf.d/,而错误日志里往往只显示 Permission denied,不会告诉你具体哪个路径被拦了。调试时先关掉这些保护项,定位清楚再逐步开。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











