# Container - PHP Laravel **Repository Path**: holyhi/container-php-laravel ## Basic Information - **Project Name**: Container - PHP Laravel - **Description**: The container environment for PHP Laravel. This image was built based on the DockerHub official image. It included the required extensions for Laravel. and included some common extensions. - **Primary Language**: Docker - **License**: MIT - **Default Branch**: master - **Homepage**: https://hub.docker.com/r/troytse/php-laravel - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-02-21 - **Last Updated**: 2026-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PHP Laravel 容器环境 面向 PHP Laravel 应用的容器环境,同时支持 Docker 与 Podman。 ## 变体 | Tag | 基础镜像 | 服务端 | 进程 | 运行用户 | `artisan` / `composer` | | --- | --- | --- | --- | --- | --- | | `apache` | [php/apache](https://hub.docker.com/_/php?tab=tags&page=1&name=apache) | Apache + mod_php | 容器内:cron、supervisor | root,应用进程降权到 `$UID`/`$GID` | `PATH` 中的包装脚本 | | `fpm-alpine` | [php/fpm-alpine](https://hub.docker.com/_/php?tab=tags&page=1&name=fpm-alpine) | PHP-FPM | 容器内:supercronic、supervisor | root,应用进程降权到 `$UID`/`$GID` | `PATH` 中的包装脚本 | | `fpm-dhi` | Docker Hardened Images(`-fpm`) | PHP-FPM | 仅 web | 非 root(uid/gid 65532) | 无 CLI、无 shell、无 Composer | | `fpm-dhi-cli` | Docker Hardened Images(`-dev`) | — | 队列 worker、`schedule:work` | 默认 root(可用 `--user` 指定) | 有 CLI + shell + Composer | 前两个是同一套设计:容器以 root 启动,按 `$UID`/`$GID` 创建名为 `container` 的用户,并可选地在容器内运行 cron 与 supervisor。这需要 root 权限,**在 rootless 模式下无法工作**。 `fpm-dhi` 是另一套设计。它以非 root 用户运行,且镜像内没有 shell、没有包管理器、没有 cron、没有 supervisor,因此容器内无法管理任何东西。队列 worker 与定时任务改为**用同一个镜像启动的独立容器**,详见[它的章节](#fpm-dhi)。 ## 特性 - 兼容 Docker 与 Podman - 三个变体,分别覆盖 mod_php、PHP-FPM 与硬化非 root 运行时 - 包含 Laravel 所需与常用的 PHP 扩展,三个变体的扩展集保持一致 - 可选启用 cron 与 supervisor 服务(`apache`、`fpm-alpine`) - 可通过 `$UID`/`$GID` 指定执行用户(`apache`、`fpm-alpine`) ## 目录 - [使用说明](#使用说明) - [apache:](#apache) - [fpm-alpine:](#fpm-alpine) - [fpm-dhi:](#fpm-dhi) - [fpm-dhi-cli:](#fpm-dhi-cli) - [环境变量](#环境变量) - [UID / GID](#uid--gid) - [SCHEDULE](#schedule) - [SUPERVISORD](#supervisord) - [Supervisor 配置](#supervisor-配置) - [控制台命令](#控制台命令) - [构建镜像](#构建镜像) - [包含的扩展](#包含的扩展) ## 使用说明 ### apache: 基于 [php/apache](https://hub.docker.com/_/php?tab=tags&page=1&name=apache) 镜像构建。增加对 Laravel 定时任务与队列的支持,并可通过 `$UID` 和 `$GID` 指定执行用户。容器将在启动时创建运行用户。 Apache、定时任务(`artisan schedule:run`)、队列 worker(`artisan queue:work`)以及 artisan 命令,都会以 `$UID` 和 `$GID` 指定的用户运行。 容器必须以 root 运行,因为它要在启动时创建该用户。`$UID` 与 `$GID` 为必填且必须大于 0。请确保挂载的宿主机目录权限与它们匹配。 #### docker ```shell ▶ sudo docker run -d \ --restart always \ --hostname simple \ --name simple -p 80:80 \ -e UID=$(id -u) \ -e GID=$(id -g) \ -e SCHEDULE=On \ -e SUPERVISORD=On \ -v /path/to/your/php.ini:/usr/local/etc/php/php.ini \ -v /path/to/host:/var/www/html \ troytse/php-laravel:apache ``` #### podman ```shell ▶ sudo podman run -d \ --restart always \ --hostname simple \ --name simple -p 80:80 \ -e UID=$(id -u) \ -e GID=$(id -g) \ -e SCHEDULE=On \ -e SUPERVISORD=On \ -v /path/to/your/php.ini:/usr/local/etc/php/php.ini \ -v /path/to/host:/var/www/html \ troytse/php-laravel:apache ``` #### `apache` 的典型示例: ```shell ▶ docker exec -it sample ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.8 0.2 223304 32828 ? Ss 10:27 0:00 apache2 -DFOREGROUND root 47 0.0 0.0 5512 2060 ? Ss 10:27 0:00 /usr/sbin/cron contain+ 58 0.0 0.0 223336 10068 ? S 10:27 0:00 apache2 -DFOREGROUND contain+ 59 0.0 0.0 223336 10068 ? S 10:27 0:00 apache2 -DFOREGROUND contain+ 60 0.0 0.0 223336 10068 ? S 10:27 0:00 apache2 -DFOREGROUND contain+ 61 0.0 0.0 223336 10068 ? S 10:27 0:00 apache2 -DFOREGROUND contain+ 62 0.0 0.0 223336 10068 ? S 10:27 0:00 apache2 -DFOREGROUND root 65 0.0 0.1 26204 16452 ? Ss 10:27 0:00 /usr/bin/python2 /usr/bin/supervisord -c /var/local/etc/supervisord.conf contain+ 66 1.0 0.2 107956 46652 ? S 10:27 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high contain+ 67 0.9 0.2 107956 46636 ? S 10:27 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high contain+ 68 1.0 0.2 107956 46968 ? S 10:27 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high contain+ 70 0.9 0.2 107956 46636 ? S 10:27 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=low contain+ 71 0.9 0.2 107956 46636 ? S 10:27 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=low root 99 0.0 0.0 7640 2732 pts/0 R+ 10:27 0:00 ps -aux ``` ### fpm-alpine: 基于 [php/fpm-alpine](https://hub.docker.com/_/php?tab=tags&page=1&name=fpm-alpine) 镜像构建。增加对 Laravel 定时任务与队列的支持,并可通过 `$UID` 和 `$GID` 指定执行用户。容器将在启动时创建运行用户。 PHP-FPM、定时任务(`artisan schedule:run`)、队列 worker(`artisan queue:work`)以及 artisan 命令,都会以 `$UID` 和 `$GID` 指定的用户运行。 与 `apache` 相同,容器必须以 root 运行,因为它要在启动时创建该用户;`$UID`/`$GID` 为必填。 ```shell ▶ sudo docker run -d \ --restart always \ --hostname sample \ --name sample \ -p 9000:9000 \ -e UID=$(id -u) \ -e GID=$(id -g) \ -e SCHEDULE=On \ -e SUPERVISORD=On \ -v /path/to/your/php.ini:/usr/local/etc/php/php.ini \ -v /path/to/host:/var/www/html \ troytse/php-laravel:fpm-alpine ``` #### `fpm-alpine` 的典型示例: ```shell ▶ sudo docker exec -it sample ps -a sample:/var/www/html# ps -A PID USER TIME COMMAND 1 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 25 root 0:00 /usr/local/bin/supercronic /usr/local/etc/crontab.txt 36 containe 0:00 php-fpm: pool www 37 containe 0:00 php-fpm: pool www 38 root 0:00 {supervisord} /usr/bin/python3 /usr/bin/supervisord -c /usr/local/etc/supervisord.conf 39 containe 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high 40 containe 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high 41 containe 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high 42 containe 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=low 43 containe 0:00 /usr/local/bin/php /var/www/html/artisan queue:work --queue=low 92 root 0:00 ps -A ``` ### fpm-dhi: 基于 Docker Hardened Images 而非官方 PHP 镜像,设计与上面两个变体不同。它以非 root 用户(uid/gid 65532)运行,且镜像内**没有 shell、没有包管理器、没有 cron、没有 supervisor、也没有 PHP CLI**,因此容器内不管理任何东西。它只负责 web: | 容器 | 命令 | 用途 | | --- | --- | --- | | `*-web` | *(镜像默认)* | php-fpm 监听 9000,置于网关或反向代理之后 | ```shell ▶ IMAGE=php-laravel:fpm-dhi ▶ CODE=/srv/myapp ▶ NET=myapp ▶ podman run -d --name myapp-web --restart always \ --user $(id -u):$(id -g) \ -v $CODE:/var/www/html \ --network $NET \ -p 9000:9000 \ $IMAGE ``` 执行用户通过 `--user` 指定(镜像无法创建用户)。挂载的应用目录必须对该 uid 可读,`storage/` 与 `bootstrap/cache/` 必须对它可写。 只有在宿主机上的进程需要直接访问 PHP-FPM 时才需要 `-p 9000:9000`。如果网关或反向代理是 `$NET` 上的另一个容器,直接用 `myapp-web:9000` 访问即可,无需发布端口。 php-fpm 既不需要 CLI,也不会派生任何子进程,所以这两者都被刻意省略:它是对外暴露端口的那个容器,保持精简正是重点。 ### fpm-dhi-cli: 除 web 之外的一切都跑在这个配套镜像上。它基于 DHI 的 `-dev` 变体构建,因此同时具备 PHP CLI **和** shell,并编译与 `fpm-dhi` 相同的扩展集。 Laravel 执行定时任务需要 shell:`schedule:work` 通过 `Process::fromShellCommandline` 派生 `schedule:run`,而每条具体任务也以同样方式派生。`-fpm` flavor 是用 `--disable-cli` 构建的且不带 shell,所以在那里调度会以 `posix_spawn() ENOENT` 失败。用宿主 cron 也绕不过去,因为 `schedule:run` 执行每条任务时同样需要 shell。 ```shell ▶ IMAGE=php-laravel:fpm-dhi-cli ▶ CODE=/srv/myapp ▶ NET=myapp ▶ podman run -d --name myapp-queue-high --restart always \ --user $(id -u):$(id -g) \ -v $CODE:/var/www/html \ --network $NET \ $IMAGE /var/www/html/artisan queue:work --queue=default,high --concurrency=3 ▶ podman run -d --name myapp-queue-low --restart always \ --user $(id -u):$(id -g) \ -v $CODE:/var/www/html \ --network $NET \ $IMAGE /var/www/html/artisan queue:work --queue=low --concurrency=2 ▶ podman run -d --name myapp-scheduler --restart always \ --user $(id -u):$(id -g) \ -v $CODE:/var/www/html \ --network $NET \ $IMAGE /var/www/html/artisan schedule:work ``` 不需要覆盖 `--entrypoint`:本镜像的默认 entrypoint 就是 `php`,而非 `php-fpm`。 `--concurrency` 会 fork 出子 worker 进程,因此进程结构与在 supervisor 下运行多个 `queue:work` 一致。`--restart always` 取代 supervisor 的进程守护,`schedule:work` 取代 cron。 #### 一次性命令 `podman exec <容器> artisan ...` 在这里不可用:镜像的 `PATH` 中没有 `artisan` 包装脚本。请改用 CLI 镜像的临时容器执行: ```shell ▶ podman run --rm \ --user $(id -u):$(id -g) \ -v $CODE:/var/www/html \ --network $NET \ $IMAGE /var/www/html/artisan migrate --force ``` 如果需要更灵活,也可以直接用 CLI 的能力: ```shell ▶ podman run --rm \ --user $(id -u):$(id -g) \ -v $CODE:/var/www/html \ --network $NET \ $IMAGE -r 'echo PHP_VERSION, PHP_EOL;' ``` 可以把它包成宿主机脚本以简化调用。该脚本运行在宿主机上(宿主机有 shell),而不是容器内,因此它就是普通的 shell 脚本: ```shell #!/usr/bin/env bash # artisan.sh - 在一次性 fpm-dhi-cli 容器中运行 artisan set -euo pipefail IMAGE="${IMAGE:-php-laravel:fpm-dhi-cli}" CODE="${CODE:-/srv/myapp}" NET="${NET:-myapp}" if [ $# -eq 0 ]; then echo "usage: $(basename "$0") [args...]" >&2 exit 2 fi exec podman run --rm \ --user "$(id -u):$(id -g)" \ -v "$CODE:/var/www/html" \ --network "$NET" \ "$IMAGE" /var/www/html/artisan "$@" ``` ```shell ▶ ./artisan.sh migrate --force ▶ ./artisan.sh queue:restart ``` `"$@"` 必须保留引号,才能让每个参数各自作为一个参数传递。这里显式指定 PHP 二进制,而不是把 `artisan` 当作 entrypoint,因为镜像内没有 `/usr/bin/env` 供其 shebang 解析。 ### Composer **`fpm-dhi-cli` 包含 Composer**,配置方式与 `apache`、`fpm-alpine` 变体完全相同:`PATH` 中放置同一份 `scripts/composer` 包装脚本、phar 位于 `/usr/local/bin`、并全局配置 packagist 镜像源。它在此可用,是因为该镜像具备 phar 所需的一切 —— shell、`git`、`unzip` 与 `/etc/ssl/certs`: ```shell ▶ podman run --rm \ --user "$(id -u):$(id -g)" \ -v "$CODE:/var/www/html" \ -w /var/www/html \ $IMAGE composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader ``` 包装脚本会挑一个当前用户可写的 `COMPOSER_HOME`,因此用 `--user` 运行时无需可写的 home 目录。 **`fpm-dhi` 不含 Composer,也无法包含。** 它是非 root 且无 shell,而该 phar 需要派生进程(处理 dist 包时用到 `git`/`unzip`),其插件在激活时同样如此。请在宿主机上运行 Composer,或使用 `composer:2` 容器,再把 `vendor/` 提供给应用: ```shell ▶ podman run --rm \ --user "$(id -u):$(id -g)" \ -v "$CODE:/app" \ -w /app \ -e COMPOSER_HOME=/app/.composer \ -e COMPOSER_REPO_PACKAGIST=https://mirrors.aliyun.com/composer/ \ composer:2 install --no-dev --no-interaction --prefer-dist --optimize-autoloader ``` ## 环境变量 `UID`、`GID`、`SCHEDULE`、`SUPERVISORD` **仅适用于 `apache` 与 `fpm-alpine` 变体**。两个 DHI 变体用另外的机制替代这四项:用户由运行时的 `--user` 指定,调度器与队列 worker 是在 `fpm-dhi-cli` 上运行的独立容器,而非容器内服务。`SERVER_TYPE` 已固化在各镜像中,不应手动设置。 ### UID / GID **(`apache` 与 `fpm-alpine` 必填,必须大于 0)** - 指定 Apache/PHP-FPM、定时任务、队列 worker 与 artisan 命令所使用的用户 ID 和组 ID。 - 容器会创建名为 `container`、使用该 UID/GID 的用户。 - 对 `apache` 而言,`$UID` 与 `$GID` 会覆盖 `$APACHE_RUN_USER` 与 `$APACHE_RUN_GROUP`。 ### SCHEDULE **(仅 `apache` 与 `fpm-alpine`。On/Off,默认 Off)** - 是否在容器内启用 `php artisan schedule:run`。在 `apache` 上会写入 crontab;在 `fpm-alpine` 上由 supercronic 运行。 ### SUPERVISORD **(仅 `apache` 与 `fpm-alpine`。On/Off,默认 Off)** - 是否在容器内启动 supervisor 服务。 ### Supervisor 配置 **(可选,仅 `apache` 与 `fpm-alpine`)** - 将自定义的 "supervisord.conf" 放在项目目录下。 - 示例: ```conf [program:queue-work-default] process_name=%(program_name)s_%(process_num)02d directory=/var/www/html command=/usr/local/bin/php /var/www/html/artisan queue:work --queue=default,high autostart=true autorestart=true user=%(ENV_RUN_USER)s numprocs=3 redirect_stderr=true [program:queue-work-low] process_name=%(program_name)s_%(process_num)02d directory=/var/www/html command=/usr/local/bin/php /var/www/html/artisan queue:work --queue=low autostart=true autorestart=true user=%(ENV_RUN_USER)s numprocs=2 redirect_stderr=true ``` ### 其他环境变量 **其他环境变量请参考基础镜像** - [php/apache](https://hub.docker.com/_/php?tab=tags&page=1&name=apache) - [php/fpm-alpine](https://hub.docker.com/_/php?tab=tags&page=1&name=fpm-alpine) - [Docker Hardened Images](https://docs.docker.com/dhi/) ## 控制台命令 在 `apache` 与 `fpm-alpine` 上,`artisan` 与 `composer` 是 `PATH` 中的包装脚本,可直接执行: ```shell ▶ docker exec -it artisan ▶ docker exec -it composer ``` 它们会以 `$UID` 和 `$GID` 指定的用户运行。你可以在项目目录里建一个 shell 脚本简化调用: - 例如创建名为 **"container_exec.sh"** 的脚本,内容如下: ```shell #!/bin/sh sudo docker exec -it your_container_name $@ ``` - 然后这样调用: ```shell ▶ ./container_exec.sh artisan make:job DoSomething ▶ ./container_exec.sh composer dump-autoload ``` `fpm-dhi` 既没有包装脚本也没有 CLI,请改用 `fpm-dhi-cli`,按[该章节](#一次性命令)中给出的临时容器方式执行。 ## 构建镜像 构建脚本位于 `scripts/build.sh`,会自动检测可用的容器运行时。 ### 使用示例 ```bash # 构建 apache 镜像(默认镜像名称:php-laravel:apache) ./scripts/build.sh apache # 构建 fpm-alpine 镜像(默认镜像名称:php-laravel:fpm-alpine) ./scripts/build.sh fpm-alpine # 构建 fpm-dhi 镜像(默认镜像名称:php-laravel:fpm-dhi) ./scripts/build.sh fpm-dhi # 构建 fpm-dhi-cli 镜像(默认镜像名称:php-laravel:fpm-dhi-cli) ./scripts/build.sh fpm-dhi-cli # 使用自定义仓库名称构建 ./scripts/build.sh /php-laravel:apache # 使用完整的域名路径构建 ./scripts/build.sh //php-laravel:apache ``` ### 自动运行时检测 脚本会按以下顺序自动检测并使用可用的容器运行时: 1. Podman(如果已安装) 2. Docker(如果没有 podman) ### 基础镜像安全 四个变体都在 `FROM` 行写死具体的 PHP 版本,补丁级别在文件里一眼可见,而不是由构建时浮动 tag 指向哪个版本决定: - `apache` → `php:8.5.12-apache-bookworm`,`fpm-alpine` → `php:8.5.12-fpm-alpine`(docker.io) - `fpm-dhi` / `fpm-dhi-cli` → `dhi.io/php:8.5.12-debian13-*` 8.5.12 修复了无需认证即可远程执行代码的严重漏洞 [CVE-2026-109618 与 CVE-2026-109619](https://www.php.net/archive/2026.php)。`apache` 是两种服务端形态里最危险的一种:mod_php 下攻击在任何应用代码执行之前就可能发生。 后续要应用新的 PHP 安全版本,就改这些 `FROM` 行和 `LABEL Version`,不要退回 `php:apache-bookworm` / `php:fpm-alpine`。 `scripts/build.sh` 对两个 docker.io 变体传 `--pull`:tag 固定的是 PHP 版本,但 docker.io 会用 Debian/Alpine 的安全更新重建同一个 tag,本地缓存的基镜像会漏掉这些更新。`dhi.io` 的 tag 不可变,因此不强制拉取。 部署前先确认镜像内的 PHP 版本: ```shell podman run --rm php-laravel:apache php -r 'echo PHP_VERSION, PHP_EOL;' ``` ### 可用的 Containerfile 构建脚本会在 `containerfiles/` 目录中查找 Containerfile: - `containerfiles/Containerfile.apache` - `containerfiles/Containerfile.fpm-alpine` - `containerfiles/Containerfile.fpm-dhi` - `containerfiles/Containerfile.fpm-dhi-cli` 如果提供了无效的标签,脚本将显示可用的 Containerfile 列表。 构建过程将创建标签为 `php-laravel:` 的镜像,其中 `` 是提供的参数(apache、fpm-alpine、fpm-dhi 或 fpm-dhi-cli)。 ## 包含的扩展 三个变体提供相同的扩展集。 |
|
|
|
| | --- | --- | --- | --- | | bcmath | Core | ctype | curl | | date | dom | exif | fileinfo | | filter | ftp | gd | gettext | | hash | iconv | intl | json | | libxml | mbstring | mysqlnd | openssl | | pcntl | pcre | PDO | pdo_mysql | | pdo_pgsql | pdo_sqlite | pgsql | Phar | | posix | readline | redis | Reflection | | session | shmop | SimpleXML | soap | | sockets | sodium | SPL | sqlite3 | | standard | sysvmsg | sysvsem | sysvshm | | tokenizer | xml | xmlreader | xmlwriter | | Zend OPcache | zip | zlib | | ## 许可证 本项目基于 MIT 许可证开源。