systemd服务启动后退出是因为execstart未前台阻塞或type配置错误;应使用type=simple配合exec前台运行、绝对路径、正确权限,并配置restart=on-failure等策略实现自动重启。

Systemd 能可靠地让自定义脚本开机启动并持续运行,但直接写 ExecStart 调用 shell 脚本极易失败——常见原因是环境变量缺失、路径不全、前台进程退出后被 systemd 当作“成功结束”而不再拉起。
为什么你的 .service 文件启动后就退出了
systemd 默认把 ExecStart 启动的进程当作“一次性服务”,如果脚本里没用 exec 占住前台、或用了 & 后台化、或执行完就 exit,systemd 会认为服务已正常终止,不会重启。尤其 shell 脚本里常见的 nohup ./myapp & 是典型陷阱。
- 确认脚本是否前台阻塞:用
exec /path/to/your/script.sh替代/path/to/your/script.sh & - 检查脚本首行是否带正确解释器(如
#!/bin/bash),且有可执行权限(chmod +x) - 避免在脚本中使用
cd相对路径;改用绝对路径,或在 service 文件中用WorkingDirectory=显式指定 - 日志里出现
main process exited, code=exited, status=0/Success就是“假成功真退出”
Type= 选错会导致守护行为完全失效
service 文件里 Type= 决定了 systemd 如何判断服务是否“就绪”和“存活”。对普通 shell 脚本,90% 场景该用 Type=simple(默认),但必须确保 ExecStart 启动的是前台长期进程;若脚本本身只做初始化然后 fork 守护,就得用 Type=forking,并配 PIDFile= 和 GuessMainPID=no。
-
Type=simple:最常用,要求ExecStart命令不返回,直到服务退出(适合exec python3 server.py这类) -
Type=forking:适用于传统 daemon(如 nginx、redis),需明确提供PIDFile=,且脚本必须真实 fork 出子进程并退出父进程 -
Type=oneshot:仅用于开机执行一次就结束的任务(如初始化磁盘),加RemainAfterExit=yes才能配合其他服务依赖
如何让脚本崩溃后自动重启
靠 Restart= 策略 + 合理的 RestartSec 和退出码控制。默认 Restart=no,即不重启。常见组合:
-
Restart=on-failure:仅当进程非 0 退出、被信号终止、超时才重启(推荐初试) -
Restart=always:不管怎么退出都重启(慎用,可能掩盖逻辑错误) -
RestartSec=5:两次重启间隔至少 5 秒,防雪崩 - 加
StartLimitIntervalSec=60和StartLimitBurst=3防止 1 分钟内连续失败 3 次后被锁定
示例关键段:
[Service] Type=simple ExecStart=/opt/myapp/launcher.sh Restart=on-failure RestartSec=5 StartLimitIntervalSec=60 StartLimitBurst=3
调试阶段必须看的三件事
别只盯着 systemctl start myapp 是否报错,很多问题藏在后台状态和日志里。
- 用
systemctl status myapp.service查看最新退出原因、主进程 PID、Active 状态(activating (auto-restart)是正在重试) - 用
journalctl -u myapp.service -n 50 -f实时追日志,比脚本里 echo 更可信(systemd 会捕获 stdout/stderr) - 临时加
Environment=PATH=/usr/local/bin:/usr/bin:/bin和Environment=HOME=/root,避免因环境变量缺失导致命令找不到
真正难的不是写完 service 文件,而是让脚本在 systemd 的沙箱里 behave like it does in your terminal——路径、权限、环境、前台语义,缺一不可。











