必须通过Caddyfile的php_worker指令启用Worker模式,路径须为绝对路径如/app/public/worker.php,配合frankenphp run启动,且入口脚本需含frankenphp_handle_request()循环调用,PID复用方可验证成功。

在 Docker 中部署 FrankenPHP 1.3 的 Worker 模式,核心不是“改 PHP 代码”,而是让 FrankenPHP 进程以常驻方式启动,并由 Caddy 主动调度——这必须通过 Caddyfile 配置 触发,不能靠单独运行 frankenphp-worker 命令实现。
必须用 Caddyfile 的 php_worker 指令
FrankenPHP 1.3 的 Worker 模式在 Docker 中生效的前提是:Caddy 主进程识别到 php_worker 指令,并据此拉起并管理 worker 子进程。直接在容器里执行 ./frankenphp-worker --script ... 是无效的——它会起一个孤立 CLI 进程,不与 Caddy 协同,发布时无法优雅滚动,日志不可控,也收不到信号。
- Caddyfile 中需明确写入(路径必须为绝对路径):
php_worker /app/public/worker.php {<br> env APP_ENV=prod<br> env APP_DEBUG=0<br> max_requests 5000<br>}
-
/app/public/worker.php必须存在且可读;Symfony/Laravel 项目建议直接使用官方提供的public/worker.php(6.3+/10.x 起内置) -
max_requests推荐设为 5000 左右,防止内存缓慢泄漏导致 worker 僵死 - 环境变量必须用
env子指令注入,不能依赖.env文件(Docker 容器中该文件通常不加载或只读一次)
Docker 启动命令要带 run,不是 serve
FrankenPHP 官方镜像(dunglas/frankenphp)默认入口是 frankenphp serve,这是标准 HTTP 模式。启用 Worker 必须显式调用 run:
立即学习“PHP免费学习笔记(深入)”;
- 正确启动方式(推荐写进
docker-compose.yml的command):
command: ["frankenphp", "run"]
- 不要写
frankenphp serve或frankenphp-worker—— 前者不加载 worker,后者脱离 Caddy 管理 - 如果使用自定义 Caddyfile,可通过
-c /path/to/Caddyfile指定,但需确保其中包含php_worker块
验证是否真正进入 Worker 模式
光看容器起来不算数,得确认 PHP 进程真的复用了:
- 查看容器日志,应出现类似:
Starting PHP worker pool with 2 workers - 在控制器中加一行:
return response()->json(['pid' => getmypid()]);,连续发起两个请求,返回的 PID 必须相同 - 若 PID 变化,说明仍是 per-request 模式,大概率是 Caddyfile 没配
php_worker,或路径写错/脚本不可执行
worker.php 要符合三层结构
哪怕 Caddy 配对了,入口脚本写错也会退化成普通请求处理:
- 循环外(初始化一次):
require_once __DIR__.'/../vendor/autoload.php'; $kernel = new Kernel($_ENV['APP_ENV'], (bool) $_ENV['APP_DEBUG']); $kernel->boot(); - 循环内(每次请求):
frankenphp_handle_request(fn() => $kernel->handle($request)); - 每次请求后(清理):
gc_collect_cycles(); unset($_SERVER['REQUEST_URI']);(FrankenPHP 不自动重置超全局) - 禁止在脚本里再 require
.env或调用Dotenv::createUnsafeImmutable()—— 环境变量必须由 Caddy 注入



















