Supervisor要求每个后台任务独立配置program段,禁止合并命令;必须设nodaemon=true使supervisord为PID 1;日志路径需存在且有写权限;Docker中须禁用守护模式并配置绝对路径日志。

多个program段必须独立定义,不能合并或复用
Supervisor 不支持“一个配置块启动多个命令”,每个后台任务(比如队列消费者、WebSocket 服务、定时轮询脚本)都得单独写一个 [program:name] 段。常见错误是试图在 command 里用 && 或 ; 连接多个 PHP 命令,这会导致只有第一个生效,后续命令被忽略,且 Supervisor 无法分别监控它们。
正确做法是为每个任务建独立配置项,例如:
[program:queue-worker] command=php /app/artisan queue:work --queue=default directory=/app user=www-data autostart=true autorestart=true startsecs=5 redirect_stderr=true stdout_logfile=/var/log/supervisor/queue.log [program:websocket-server] command=php /app/server.php directory=/app user=www-data autostart=true autorestart=true stopwaitsecs=15 redirect_stderr=true stdout_logfile=/var/log/supervisor/ws.log
-
autostart=true和autorestart=true必须显式设置,否则新增配置后 reload 不会自动拉起 - 不同任务的
stdout_logfile路径不能相同,否则日志会互相覆盖 - 如果任务有启动依赖(比如先等 Redis 就绪),不能靠
depends_on(Supervisor 本身不支持),得在脚本里加重试逻辑或用startretries+startsecs配合判断
supervisord 必须作为 PID 1 运行,否则子进程收不到信号
在非容器环境(如物理机或虚拟机),Supervisor 默认以守护进程模式启动(nodaemon=false),此时它自己不是 PID 1,而是 fork 出子进程再退出。这会导致:子 PHP 进程无法收到 SIGTERM,supervisorctl stop 变成暴力 kill,数据可能丢失。
解决方法是强制前台运行:
- 修改主配置
/etc/supervisord.conf中的[supervisord]段:nodaemon=true - 或者启动时加参数:
supervisord -c /etc/supervisord.conf -n(-n等价于nodaemon=true) - systemd 用户需改写 service 文件,把
Type=forking改成Type=simple,并去掉PIDFile=
验证是否成功:运行 ps -o pid,ppid,comm -C supervisord,第一列 PID 应为 1。
日志路径和权限问题最容易导致启动静默失败
Supervisor 启动某个 program 失败时,常不报错,只在自身日志里记一行 spawnerr: unknown error。90% 是因为日志目录不存在、无写权限,或 user 指定错误。
- 确保
stdout_logfile所在目录存在且目标用户可写,例如:sudo mkdir -p /var/log/supervisor && sudo chown www-data:www-data /var/log/supervisor -
user字段必须是系统真实存在的非 root 用户(如www-data),不能写错大小写或拼错 - 如果 PHP 脚本需要读取项目文件,
directory必须设为项目根目录,否则相对路径会失效 - 避免用
~或环境变量(如$HOME)写路径,Supervisor 不展开它们
Docker 容器里必须禁用守护模式并确保日志落盘
在 Docker 中,Supervisor 若以默认方式启动(nodaemon=false),会 fork 后退出,容器立刻停止——因为没有 PID 1 进程存活。
关键配置只有两条:
-
[supervisord]段中必须有nodaemon=true -
command必须用绝对路径,且不能带sh -c包裹(会绕过 Supervisor 的进程管理)
另外,Docker 默认挂载的 /dev/stdout 在 Supervisor 下不可写,所以必须显式配置 stdout_logfile 到容器内可写路径(如 /var/log/supervisor/xxx.log),否则日志全丢。
容易被忽略的是:所有子进程的标准输出默认由 Supervisor 统一接管,如果你在 PHP 脚本里又手动 error_log() 到文件,可能和 Supervisor 日志冲突,建议统一走 stdout 并由 Supervisor 重定向。










