本地开发优先用 Docker 镜像,生产部署且追求极致轻量和隔离时再考虑二进制;dunglas/frankenphp 镜像省心免编译,一行命令即可运行 Laravel 等标准 PHP 应用,支持 HTTP/3、自动 HTTPS,而二进制仅适用于单文件交付场景,构建复杂、不可热更新、调试困难。

直接说结论:本地开发优先用 Docker 镜像,生产部署且追求极致轻量和隔离时再考虑二进制。
本地开发用 dunglas/frankenphp 镜像是最省心的选择
你不需要编译、不用装 Go 或 PHP 依赖、不操心 musl/glibc 兼容性问题。官方镜像已经预装 Caddy、PHP 运行时、OPcache 和常用扩展,docker run -v $PWD:/app -p 80:80 dunglas/frankenphp 一行就跑起来 Laravel 或任何标准 PHP 应用。
常见错误现象包括:file not found: /app/public/index.php —— 检查是否挂载了正确目录(必须是项目根目录,不是 public/ 子目录);permission denied —— Alpine 镜像默认以非 root 用户运行,确保 public/ 和 storage/ 目录可读写。
优势明显:
立即学习“PHP免费学习笔记(深入)”;
- Caddyfile 可通过挂载或环境变量动态注入,改配置不用重构建
- 支持
-p 443:443/udp直接启用 HTTP/3,Nginx+PHP-FPM 组合得额外配 QUIC - 本地 HTTPS 自动签发,访问
https://localhost即可,不用手动处理证书
二进制只在明确需要“单文件交付”时才值得投入
所谓“打包成二进制”,本质是把 FrankenPHP 二进制 + PHP 应用代码 + Caddyfile 全部塞进一个可执行文件里。它适合的场景非常具体:交付给客户现场部署、嵌入硬件设备、或 CI/CD 流水线中要求零依赖分发。
但代价不小:
- 构建过程依赖 Docker(即使目标是 macOS,也得用 Linux 容器交叉编译)
-
EMBED=dist/app/路径必须精确,漏掉vendor/或.env就会启动失败 - 无法热更新 Caddyfile 或 PHP 代码——改一行就得重新构建整个二进制
- 调试困难:
strace或gdb对静态链接的 FrankenPHP 效果有限,日志只能靠--log-level debug
典型误操作:直接对未清理的开发目录执行 build-static.sh,结果二进制里塞进了 node_modules/、.git/、测试用例,体积暴涨 200MB 以上。
frankenphp php-server 命令适合快速验证,但别当正式方案
在已安装 FrankenPHP 二进制的机器上,frankenphp php-server 确实能秒启服务,但它默认监听 127.0.0.1:8000、无 TLS、无进程管理、无静态文件缓存策略——这只是一个开发辅助命令,不是生产可用的服务模式。
它和 Docker 镜像里的行为也不一样:镜像内默认走 Caddy 的完整 HTTP 栈,而裸调 php-server 绕过了 Caddy 的路由、重写、Header 处理等能力,.htaccess 风格规则完全失效。
如果你发现 php-server 能跑通但换到 Docker 就 404,大概率是没注意 Caddyfile 中 root * /app/public 的路径映射逻辑,或者没设置 php_server 指令。
真正容易被忽略的一点:FrankenPHP 的“常驻内存”能力(Worker 模式)在 Docker 镜像里默认关闭,需要显式加 --worker 参数启动;而二进制打包时若没在 Caddyfile 里写 php_worker,哪怕生成了二进制,PHP 依然每次请求都重启——性能优势直接归零。



















