生产环境首选 dunglas/frankenphp 官方镜像,预装 php 8.5、caddy、opcache 等,挂载代码+设 server_name+挂 /data 卷即可运行;静态二进制构建需用 static-builder-musl 镜像并显式指定平台,扩展启用须修改 php.ini 并重启容器。

用 dunglas/frankenphp 官方镜像直接挂载代码最省事
生产环境首选 dunglas/frankenphp 官方镜像,它不是“基础镜像”,而是开箱即用的完整运行时:PHP 8.5、Caddy、Opcache、PDO、cURL 全部预装,默认启用 php.ini-production,连自动 HTTPS 和 HTTP/3 都已配好。你只需把代码挂进去,设对环境变量,就能跑。
常见错误现象:404 或 ERR_SSL_PROTOCOL_ERROR —— 多半是没设 SERVER_NAME 或漏挂 /data 卷。
-
SERVER_NAME必须设为真实域名(如example.com),不能是localhost或 IP,否则 Let's Encrypt 申请失败 - 必须挂载
caddy_data:/data卷,否则证书无法持久化,重启后反复申请会触发 ACME 速率限制 - Laravel/Symfony 项目只应
COPY public/和.env,别把storage/logs、.env.example这类敏感或冗余文件暴露进容器 - 在
docker-compose.yml中加restart: unless-stopped,避免异常退出后服务静默中断
多阶段构建静态二进制镜像(适合资源受限或离线场景)
如果你需要极致轻量、零依赖、可跨机器复制的部署包,就得走多阶段构建 + static-builder-musl 镜像这条路。这不是“优化镜像”,而是生成一个单文件二进制(frankenphp),连 libc 都打进去,ldd 显示 not a dynamic executable 才算成功。
关键实操点:不能在宿主机上编译,也不能用 glibc 环境(Ubuntu/macOS 直接跑 build-static.sh 必然失败);必须用 dunglas/frankenphp:static-builder-musl 镜像,并显式锁定平台。
- Dockerfile 开头必须写
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-musl(ARM64 则换为linux/arm64) - 扩展安装只能用
RUN docker-php-ext-install opcache pdo_mysql mbstring,pecl install不可用(镜像没带 pecl) -
EMBED路径要干净,推荐COPY前先composer install --no-dev --optimize-autoloader --ignore-platform-reqs,再只COPYpublic/、vendor/、bootstrap/等必要目录 - 构建产物验证命令:
file ./frankenphp应显示aarch64或x86-64+statically linked;ldd ./frankenphp必须报not a dynamic executable
自定义 Dockerfile 里启用扩展但不重启容器?别信
有些文档说“装完扩展 reload php 就行”,在 FrankenPHP 里这行不通。尤其是 opcache 这类依赖 php.ini 生效的扩展,光执行 install-php-extensions 或 docker-php-ext-install 是不够的——它只是把 .so 文件放进去,但 PHP 进程根本不会加载。
根本原因:FrankenPHP 默认加载 $PHP_INI_DIR/php.ini,而官方镜像初始只有 php.ini-production,没重命名为 php.ini,也没做任何 extension= 启用操作。
- 必须显式激活配置:
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" - 如果要用自定义配置项(比如
opcache.enable=1),得写进php.ini或通过php_ini指令在 Caddyfile 的frankenphp { }块里设置 - 即使改了配置,也必须重启容器,因为 FrankenPHP 启动时就完成了 PHP 模块初始化,运行中无法热加载扩展
别被 40–60 MB 的二进制体积吓住
一个 Laravel + SQLite 静态二进制打包出来 50 MB 左右是常态,里面塞了整个 PHP 运行时、Caddy、libphp、所有启用的扩展和你的代码。但这只是磁盘体积,不是内存压力。
实际运行时 RSS(常驻内存)通常只有 15–25 MB,远低于传统 Nginx + PHP-FPM 组合。真正容易被忽略的是构建路径污染:比如 COPY . . 把 node_modules/、.git/、tests/ 全塞进去,不仅体积暴增,还可能因混入 x86_64 编译的 PECL 扩展导致 ARM64 上运行时报 undefined symbol。
构建前务必清理,验证时别只看能不能启动,要进容器执行 php -m | grep opcache 和 php --ri pdo_mysql 确认扩展真被加载了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











