根本原因是supervisor默认以root等非部署用户启动,导致读不到.env环境变量或无法连接redis/db;需显式配置environment、user、directory及日志,并设autorestart=unexpected和exitcodes=0,2。

Supervisor 启动后队列没消费,php artisan queue:work 手动跑却正常
根本原因是 Supervisor 默认用 root 或其他非部署用户启动进程,导致读不到你的应用环境变量(比如 .env 里的 QUEUE_CONNECTION),或者连不上 Redis/DB。它跑的不是你本地测试时那个上下文。
实操建议:
- 在 Supervisor 配置里显式指定
environment,例如:environment=APP_ENV="production",QUEUE_CONNECTION="redis" - 确保
user配置项和你的部署用户一致(比如www-data或deploy),别留空或写成root - 检查
directory是否指向项目根目录(含artisan的那层),否则artisan找不到配置 - 加一句
stdout_logfile=/var/log/supervisor/queue.log,出问题直接看日志,比猜快得多
Laravel 队列任务失败但 Supervisor 不报错、不重启
Supervisor 默认只管 PHP 进程是否活着,不管 Laravel 内部有没有抛异常、任务是否卡死或反复失败。它看到 queue:work 进程还在,就认为一切正常。
实操建议:
- 强制 Supervisor 检查子进程退出码:加上
autorestart=unexpected和exitcodes=0,2(Laravel 队列 worker 正常退出是 0,异常退出通常是 2) - 给
queue:work加参数防卡死:--max-jobs=100 --max-time=3600,避免单个 worker 跑太久、内存泄漏或被 OOM kill 后静默退出 - 别依赖
--daemon(已废弃),用--once+ Supervisor 的自动重启机制更稳
Redis 队列延迟高,php artisan queue:work 明明在跑但任务积压
常见于 Supervisor 启了多个 queue:work 进程,但 Redis 连接数不够,或连接未复用,大量 TIME_WAIT 占满端口;也可能是任务里同步调用了慢 HTTP 请求,阻塞了整个 worker。
实操建议:
- 确认 Supervisor 的
numprocs和 Redis 最大连接数匹配(比如redis.conf里maxclients设为 200,那numprocs别超 10,留余量) - 在
config/queue.php的redis配置里加'persistent' => true(Laravel 9+ 支持),减少连接开销 - 把耗时操作(如发邮件、调第三方 API)拆进新任务,别在一个
handle()里串行干完
Supervisor reload 后队列进程没更新代码
Supervisor 只负责拉起进程,不负责热更。改完代码后,旧的 queue:work 进程还拿着老的类加载器和 opcode 缓存,除非它自己退出再被拉起。
实操建议:
- 每次部署后必须执行:
supervisorctl stop laravel-worker && supervisorctl start laravel-worker,不能只reload - 在部署脚本里加钩子:部署完自动触发
supervisorctl reread && supervisorctl update,再restart - 如果用 Envoyer 或 Laravel Envoy,确保
after阶段包含进程重启命令,漏掉这步等于白部署
最常被忽略的是环境变量隔离和进程生命周期管理——Supervisor 不是黑盒,它启动的每个 queue:work 都得当成独立的 CLI 环境来对待,而不是“配好就完事”。











