直接用systemd管理uvicorn是生产推荐做法,因nohup与&会导致进程脱钩、systemd误判退出、日志脱离journalctl;正确配置需设Type=simple、StandardOutput=journal、Restart=always,并验证权限、模块导入和实时日志。

直接用 systemd 管理 uvicorn 是生产环境的推荐做法,比 nohup 更可靠、可查、可依赖。它能自动拉起崩溃进程、按需等待网络就绪、统一记录日志,且不依赖终端会话。
为什么不能直接用 nohup 启动方式写进 systemd
很多人把原来 nohup /path/to/uvicorn ... & 的命令原样塞进 ExecStart,结果服务启动失败或状态显示 inactive (dead)。根本原因是:nohup 和 & 在 systemd 的 Type=simple 模式下会造成进程“脱钩”——systemd 启动后立即认为服务已退出,实际子进程却在后台跑着,导致无法监控、无法重启、日志也不进 journalctl。
-
nohup是为交互式 shell 设计的,和systemd的进程生命周期模型冲突 -
&让 shell fork 出子进程后立刻返回,systemd误判为主进程已终止 - 日志重定向(如
> uvicorn.log 2>&1)会绕过journald,失去集中日志能力
正确的 service 文件结构(以 FastAPI 为例)
假设你的项目路径是 /workspase/python/ai_demo/app/backend,虚拟环境在 /workspase/python/ai_demo/app/venv,入口模块是 app.main:app,监听 0.0.0.0:8000:
[Unit] Description=FastAPI Backend Service After=network.target Wants=network-online.target [Service] Type=simple User=your-username Group=your-username WorkingDirectory=/workspase/python/ai_demo/app/backend Environment="PATH=/workspase/python/ai_demo/app/venv/bin" ExecStart=/workspase/python/ai_demo/app/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload-dir /workspase/python/ai_demo/app/backend Restart=always RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
-
Type=simple:因为uvicorn主进程不 fork,直接阻塞运行,这是最匹配的行为 -
Environment="PATH=..."比ExecStartPre=source venv/bin/activate更干净,避免 shell 解析问题 - 去掉
--reload(开发用),生产环境应使用--reload-dir或干脆不用;若需热更,建议配合git pull+systemctl restart -
Restart=always确保崩溃后自动拉起,RestartSec=5避免密集重启
常见启动失败原因与验证步骤
执行 sudo systemctl start your-service-name.service 后,如果没起来,别急着改配置,先看这三件事:
- 检查路径权限:
your-username是否对/workspase/python/ai_demo/app/venv/bin/uvicorn和app.main:app所在目录有读+执行权限?ls -l看一眼 - 确认 Python 模块可导入:
sudo -u your-username /workspase/python/ai_demo/app/venv/bin/python -c "import app.main",报错就说明路径或__init__.py缺失 - 查实时日志:
sudo journalctl -u your-service-name.service -f,不是看uvicorn.log—— 那个文件此时已被忽略 - 注意
After=network.target只保证网络接口启动,如需 DNS 或外网可达,加Wants=network-online.target并确保systemd-networkd-wait-online.service已启用
真正难的不是写对第一行 ExecStart,而是让 systemd 完全信任这个进程:它得知道谁是主进程、什么时候算“启动成功”、崩溃时怎么收尸。这些细节一旦漏掉,服务看似在跑,实则游离在监控之外——比如 OOM 被杀后静默消失,连告警都触发不了。


















