Laravel的schedule:run是单次命令,依赖服务器Cron每分钟触发以检查并分发任务,本身不常驻;queue:work默认处理一个任务即退出,生产环境须用supervisord等进程管理器守护。

部署 Laravel 时,队列和定时任务不会自动“带上”——它们是独立于 Web 请求的长期运行进程,必须显式启动、守护并正确关联环境。漏掉任一环节,schedule:run 就只跑一次,queue:work 会退出或不执行。
为什么 php artisan schedule:run 每分钟只触发一次就停了
因为它是单次命令,不是守护进程。服务器 Cron 只负责每分钟拉起它一次,而它本身只做“检查+触发”,不接管后续任务执行。
-
schedule:run不运行任务本身,只调用->command()或->job()定义的逻辑;如果该逻辑是同步执行(比如没走队列),那确实能马上完事;但如果写的是->job(YourJob::class),那只是把任务推入队列,真正执行还得靠queue:work - 常见错误:只配了 Cron,却没跑
queue:work,结果所有带->job()的调度都卡在队列里不动 - Cron 条目必须用绝对路径:
* * * * * /usr/bin/php /var/www/html/artisan schedule:run >> /dev/null 2>&1,否则可能因 PATH 或工作目录不对而失败
为什么 php artisan queue:work 启动后很快就退出
它默认只处理一个任务就结束(除非加 --once 显式指定),且不自动重载代码、不捕获异常退出、不处理子进程崩溃。
- 生产环境绝不能直接前台运行
queue:work;必须用进程管理器(如supervisord)维持常驻,并配置自动重启 -
QUEUE_CONNECTION必须和.env里一致,且驱动已启用(比如用redis就得确认 Redis 服务可达,database就得先php artisan queue:table && php artisan migrate) - 如果任务里抛了未捕获异常,
queue:work进程会直接终止——supervisord能拉起新进程,但你得看日志才能发现是哪条任务炸了
Docker 环境下怎么让两者同时活起来
Docker 默认只允许一个主进程(PID 1),所以不能在一个容器里既跑 Nginx 又跑 queue:work 又跑 cron ——必须拆成多个服务,各自专注一件事。
- Web 容器:只跑 PHP-FPM + Nginx,不碰队列或调度
- Cron 容器:轻量级镜像(如
alpine:latest),挂载artisan,用crond -f执行schedule:run(注意:Docker 内crond不支持用户级 crontab,得写进/etc/crontabs/root或用脚本轮询) - Queue 容器:基于 PHP 镜像,启动时直接执行
php artisan queue:work --sleep=3 --tries=3,由supervisord或 Docker 的 restart policy 管理生命周期 - 三者共享同一份
.env和 Redis/DB 配置,否则队列推进去、worker 拉不出来
最容易被忽略的权限与环境一致性
本地测试通了,一上生产就失灵,八成栽在这几处:
-
storage/logs/和bootstrap/cache/目录权限不是www-data(或你设的运行用户)可写,导致config:cache失败、日志写不进、队列 worker 启动报错 -
.env里QUEUE_CONNECTION=redis,但生产 Redis 地址填的是127.0.0.1——容器内根本连不到宿主机的 127.0.0.1,得换成服务名(如redis) - 用了 Horizon 却没开
horizon:supervisor,或者 Supervisor 配置里写的queue:work命令没加--queue=high,default,结果低优先级队列永远没人消费 - Git 部署后没清缓存:
php artisan config:clear是必须的,否则.env修改不生效,队列还连着开发库


















