supervisor 启动 java 进程失败主因是 command 未用绝对路径或 java 环境不可用,须配 autorestart=true、startsecs=10、redirect_stderr=true、stdout_logfile,并启用 stopasgroup=true 和 killasgroup=true 以正确管理进程组。

Supervisor 启动 Java 进程时提示 ERROR (no such file) 或直接退出
根本原因通常是 command 路径写错,或 Java 环境在 Supervisor 的子 shell 中不可用。Supervisor 不继承系统用户的 $PATH,也不加载 ~/.bashrc 或 /etc/profile。
- 必须用绝对路径写
command:比如/usr/bin/java -jar /opt/myapp/app.jar,不能只写java -jar app.jar - 检查 Java 是否真在该路径:
which java,多数生产环境是/usr/bin/java或/usr/lib/jvm/java-17-openjdk-amd64/bin/java - 如果依赖自定义
JAVA_HOME,得显式传入:environment=JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64" - 避免用
sudo启动 Java —— Supervisor 本身以 root 运行,子进程默认也用 root,但 Java 应用通常不该用 root 跑;加user=deploy更安全
Java 进程启动后立刻被 Supervisor 杀掉(EXITED 状态)
这是最常被忽略的点:Supervisor 默认把 command 当作「短命命令」处理,而 Java 应用(尤其是 Spring Boot)是长运行服务,需要明确告诉 Supervisor “别当它结束了”。
- 必须设置
autorestart=true和startsecs=10(不能是 0),否则进程一“没立刻输出”就被判失败 - Spring Boot 应用要加
-Dspring.profiles.active=prod等参数确保不卡在交互式配置里 - 关键:加
redirect_stderr=true和stdout_logfile=/var/log/myapp/out.log,不然日志全丢,你根本看不到为什么崩了 - 如果 Java 进程内部自己 fork 出后台子进程(比如某些老框架用
nohup),Supervisor 就管不住它了——必须禁用这种模式,让 Java 进程始终是前台进程
Supervisor 配置里要不要写 stopasgroup=true 和 killasgroup=true
要,而且几乎必须开。Java 应用一旦用了线程池、Netty、或 JVM 自带的信号处理器,简单 kill $PID 很可能杀不死,只杀主进程,子线程还在跑。
-
stopasgroup=true让 Supervisor 对整个进程组发SIGTERM,不是单个 PID -
killasgroup=true在超时后补发SIGKILL,同样作用于整个组 - 配合
stopwaitsecs=30给足时间让 Spring 的SmartLifecycle或@PreDestroy正常执行 - 注意:这两个选项在旧版 Supervisor(
Java 日志滚动和磁盘占满风险
Supervisor 自带的日志轮转(stdout_logfile_maxbytes)只管自己的 wrapper 日志,不管 Java 应用自己打的 log(比如 Logback 的 RollingFileAppender)。两者不联动,容易重复占用空间。
- 关掉 Supervisor 的日志截断:
stdout_logfile_maxbytes=0,让它只追加,不轮转 - Java 应用内务必配好日志框架的滚动策略,比如 Logback 的
<timebasedfilenamingandtriggeringpolicy></timebasedfilenamingandtriggeringpolicy> - 如果用
stdout_logfile收集控制台输出,记得设stdout_logfile_backups=5,不然一个 2GB 的out.log永远不清理 - 定期用
logrotate管理/var/log/supervisor/下的 supervisor 自身日志,别指望它自己搞定所有层级
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











