Webman 官方 docker-compose.yml 开箱即用但不可直接改端口上线,因缺失 restart: unless-stopped 和 runtime 目录挂载,导致重启后日志丢失、路由缓存失效;且默认镜像禁用 pcntl/posix 扩展,需换 CLI 镜像或手动编译,同时须确保 vendor 正确复制、ENTRYPOINT 为 exec 模式并配置 init: true 以支持信号传递与权限管理。

Webman 容器化部署不依赖 Docker Compose 也能跑,但官方 docker-compose.yml 是开箱即用的最简路径;直接 docker run 启动容易漏掉 pcntl、posix 等扩展或信号处理逻辑,导致热重载失效、进程无法优雅退出。
webman 的 docker-compose.yml 为什么不能直接改端口就上线
官方生成的 docker-compose.yml 默认暴露容器内 8787 端口,但 Webman 实际监听的是 0.0.0.0:8787 —— 这本身没问题。真正卡住的地方是:它没声明 restart: unless-stopped,也没挂载 runtime/ 目录。一旦容器重启,runtime/log 和 runtime/cache 全丢,日志没了,路由缓存也炸了。
- 必须手动在
php服务块里加restart: unless-stopped -
volumes至少要映射./runtime:/app/runtime,否则每次重启都重刷缓存 - 若改宿主机端口(比如从
8787:8787改成8080:8787),别忘了检查防火墙和反向代理(如 Nginx)是否放行新端口
php:8.1-fpm 镜像跑 webman 会报错 pcntl_fork() is disabled
Webman 启动时默认启用多进程模式(start.php start -d),而官方 PHP-FPM 镜像默认禁用 pcntl 和 posix 扩展——这是安全限制,不是 bug。光靠 docker-php-ext-enable pcntl 不行,因为镜像里压根没编译这个模块。
- 要么换基础镜像:用
php:8.1(CLI 版)而非php:8.1-fpm,Webman 本质是常驻 CLI 进程,不需要 FPM - 要么自己编译:在
Dockerfile里加RUN docker-php-ext-install pcntl posix,再确认php -m | grep pcntl有输出 - 生产环境建议关多进程:
start.php start -d --process=1,避免扩展依赖,也更利于容器资源控制
docker build 后 php start.php start 报 Class 'Workerman\Worker' not found
这是典型的自动加载失败,根本原因是 Composer 依赖没进镜像,或者工作目录不对。Webman 项目结构里 vendor/ 不该被 .dockerignore 忽略,但本地开发时又常把 vendor/ 加进 gitignore —— 构建时若没执行 composer install,类自然找不到。
立即学习“PHP免费学习笔记(深入)”;
- 确保
Dockerfile中有RUN composer install --no-dev --optimize-autoloader(注意不是require) -
WORKDIR必须设为/app(Webman 默认路径),且COPY . .前不能有COPY composer.json composer.lock ./分步复制(会破坏 layer cache) - 如果用了多阶段构建,最后 stage 必须
COPY --from=builder /app/vendor /app/vendor,不能只复制代码
如何让 webman 容器支持 signal 传递(比如 docker stop 正常退出)
Webman 依赖 SIGINT 或 SIGTERM 触发 Worker::stopAll(),但默认 docker run 启动的容器,主进程是 shell,信号根本传不到 PHP 进程。你执行 docker stop,等 10 秒强制 kill,连接池、定时器、未 flush 的日志全丢。
- 必须用
exec模式启动:Dockerfile 末尾写ENTRYPOINT ["php", "start.php", "start", "-d"],不要套sh -c - 验证方式:
docker exec -it <container> ps aux</container>,看到php start.php start -d是 PID 1,才算成功 - 如果用
docker-compose.yml,还要加init: true(启用 tini 初始化进程),防止僵尸进程累积
最易忽略的其实是 runtime/ 目录权限:容器内 www-data 用户可能没写入权限,导致 log 目录创建失败、缓存写不进去,错误还不报——得进容器手动 chown -R www-data:www-data runtime 才能暴露问题。



















