Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions .github/workflows/legacy-v5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ jobs:
strategy:
fail-fast: false
matrix:
php_version: ['8.1', '8.0', '7.4']
php_version: ['8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.4']
variant: ['apache','cli','fpm']
builder: [ {arch: "amd64", os: "ubuntu-24.04"}, {arch: "arm64", os: "ubuntu-24.04-arm"}]
runs-on: ${{ matrix.builder.os }}
name: Build ${{ matrix.php_version }}-${{ matrix.variant }} ${{ matrix.builder.arch }}
env:
# Non-maintained PHP versions: all the images. Maintained PHP versions: only the non-maintained NodeJS versions
BAKE_GROUP: php${{ matrix.php_version }}-${{ matrix.variant }}-${{ contains(fromJSON('["8.1", "8.0", "7.4"]'), matrix.php_version) && 'all' || 'legacy-node' }}
name: Build&test ${{ matrix.php_version }}-${{ matrix.variant }} ${{ matrix.builder.arch }}
steps:
- name: Checkout
uses: actions/checkout@v7
Expand All @@ -26,35 +29,49 @@ jobs:
uses: docker/setup-buildx-action@v4
- name: Build
run: |
PHP_VERSION="${{ matrix.php_version }}"
# Get last cached successful builds
TARGETS=$(REPO="ghcr.io/${{ github.repository_owner }}/php" TAG_PREFIX="" docker buildx bake php${PHP_VERSION//.}-${{ matrix.variant }}-all --progress=quiet --print | jq -r '.target[].tags[]')
for TARGET in $TARGETS; do
docker pull --quiet ${TARGET} || echo ""
done;
# Build the current builds
REPO="ghcr.io/${{ github.repository_owner }}/php" \
ARCH_SUFFIX="-${{ matrix.builder.arch }}" \
TAG_PREFIX="rc${GITHUB_RUN_ID}-" \
docker buildx bake \
--set "*.platform=linux/${{ matrix.builder.arch }}" \
--set "*.output=type=registry" \
--progress=quiet \
--load \
php${PHP_VERSION//.}-${{ matrix.variant }}-all
--set "*.output=type=docker" \
--load --progress=quiet \
${BAKE_GROUP//.}
- name: Display tags built
run: |
docker image ls --filter="reference=ghcr.io/${{ github.repository_owner }}/php" --format "{{.CreatedAt}}\t{{.Size}}\t{{.Repository}}:{{.Tag}}"
- name: Test
run: |
REPO="ghcr.io/${{ github.repository_owner }}/php" \
TAG_PREFIX="rc${GITHUB_RUN_ID}-" \
PHP_VERSION="${{ matrix.php_version }}" \
ARCH_SUFFIX="-${{ matrix.builder.arch }}" \
BRANCH="v5" \
VARIANT="${{ matrix.variant }}" \
PLATFORM="linux/${{ matrix.builder.arch }}" \
./tests-suite/bash_unit -f tap ./tests-suite/*.sh
- name: Publish specific arch version
run: |
TAGS=$(REPO="ghcr.io/${{ github.repository_owner }}/php" \
ARCH_SUFFIX="-${{ matrix.builder.arch }}" \
TAG_PREFIX="rc${GITHUB_RUN_ID}-" \
docker buildx bake \
--print --progress=quiet \
${BAKE_GROUP//.} | jq -r '.target[].tags[]');
for TAG in $TAGS; do docker push --quiet ${TAG}; done;
merge-and-publish:
needs:
- build
runs-on: ubuntu-latest
env:
# Non-maintained PHP versions: all the images. Maintained PHP versions: only the non-maintained NodeJS versions
BAKE_GROUP: php${{ matrix.php_version }}-${{ matrix.variant }}-${{ contains(fromJSON('["8.1", "8.0", "7.4"]'), matrix.php_version) && 'all' || 'legacy-node' }}
name: Merge and publish ${{ matrix.php_version }}-${{ matrix.variant }}
strategy:
fail-fast: false
matrix:
php_version: ['8.1', '8.0', '7.4']
php_version: ['8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.4']
variant: ['apache','cli','fpm']
steps:
- name: Checkout
Expand Down Expand Up @@ -88,18 +105,19 @@ jobs:
- name: Get list of images for this variant
id: get-images
run: |
PHP_VERSION="${{ matrix.php_version }}"
REPO="ghcr.io/${{ github.repository_owner }}/php" \
TAG_PREFIX="rc${GITHUB_RUN_ID}-" \
docker buildx bake \
--print --progress=quiet \
php${PHP_VERSION//.}-${{ matrix.variant }}-all | jq '.target[].tags[]'
${BAKE_GROUP//.} | jq '.target[].tags[]'
- name: Create multiarch manifests
run: |
PHP_VERSION="${{ matrix.php_version }}"
# Get all targets from the bake group
TARGETS=$(docker buildx bake php${PHP_VERSION//.}-${{ matrix.variant }}-all --progress=quiet --print | jq -r ".group[\"php${PHP_VERSION//.}-${{ matrix.variant }}-all\"].targets[]")
TARGETS=$(docker buildx bake ${BAKE_GROUP//.} --progress=quiet --print | jq -r ".group[\"${BAKE_GROUP//.}\"].targets[]")
# The slim and fat images of the maintained PHP versions are published by the main workflow
if [[ "${BAKE_GROUP}" == *-legacy-node ]]; then
TARGETS=$(echo "${TARGETS}" | grep -- '-node')
fi
for TARGET in $TARGETS; do
TAG_ORI=$(REPO="ghcr.io/${{ github.repository_owner }}/php" TAG_PREFIX="rc${GITHUB_RUN_ID}-" docker buildx bake ${TARGET} --progress=quiet --print | jq -r ".target | to_entries[] | select(.key == \"${TARGET}\") | .value.tags[0]")
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

### Minor changes

* **2026-10-21**
* Security: fix of php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) in all the v5 amd64 images. PHP 7.4, 8.0 and 8.1 images are rebuilt with the backport of the fix (same PHP version: 7.4.33, 8.0.30, 8.1.34). The arm64 images will be fixed when the fixed PHP packages are published for arm64. See [php2shell](docs/security-php2shell.md)
* NodeJS 18 and 20 variants are built again for all the PHP versions, by the legacy workflow
* New test checking that the images are not vulnerable to php2shell (only a warning on arm64 until the fixed packages are published)
* Legacy build (`legacy-v5.yml`): the images are tested before being published, like in the main build
* PHP 8.0 and 8.1: the `swoole` extension is removed, packages.sury.org does not provide it for these versions
* The build of an extension fails when its package does not exist for the PHP version, instead of installing the package of another PHP version

* **2026-10-19**
* New documentation page for the fpm variant: serving HTTP (built-in Apache or another web server), constraints of PHP-FPM and migration from the apache variant
* fpm variant: the time the built-in Apache waits for PHP-FPM can be configured with `APACHE_PROXY_TIMEOUT` (300 seconds by default)
Expand Down
23 changes: 7 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,12 @@

# General purpose PHP images for Docker

> **New: built-in Apache in the fpm variant (`PHP_FPM_WEB_SERVER=apache`)**
> **Security: php2shell (CVE-2026-109618 and CVE-2026-109619, CVSS 9.2) is fixed in all the v5 amd64 images**
>
> The *fpm* variant can now run Apache in front of PHP-FPM, in the same container. It is a drop-in alternative to the
> *apache* variant: same Apache features (`.htaccess`, `APACHE_DOCUMENT_ROOT`, `APACHE_EXTENSION_*`), but PHP runs in
> PHP-FPM instead of `mod_php`.
>
> **Why?** Under the same load ([benchmark](benchmarks/fpm-apache)), the container serves **2 to 3 times more traffic
> with 4 times less memory** (30 vs 10 pages/s on 2 CPUs, ~90 vs ~380 MiB). `mod_php` forces Apache to dedicate a
> whole process embedding PHP to each connection, including idle keep-alive connections and static files. With
> PHP-FPM, Apache uses its threaded MPM (`mpm_event`) and only PHP requests reach the PHP workers.
>
> ```bash
> $ docker run -p 80:80 -e PHP_FPM_WEB_SERVER=apache -v "$PWD":/var/www/html thecodingmachine/php:8.4-v5-fpm
> ```
>
> **[Read more: advantages, constraints of PHP-FPM and migration from the *apache* variant](docs/fpm.md)**
> (for instance, `php_value` directives are not supported in `.htaccess` files). The *apache* variant is still available.
> Pull the images again, recreate your containers and rebuild the images built from ours. The **arm64** images
> (Apple Silicon, AWS Graviton...) are **not fixed yet**: the fixed PHP packages for arm64 are still being built
> upstream, and the images will be updated as soon as they are published. The v4 images are **not** fixed: migrate to
> v5. See [what to do](docs/security-php2shell.md).
This repository contains a set of developer-friendly, general purpose PHP images for Docker.

Expand Down Expand Up @@ -155,6 +144,7 @@ Bugs are fixed on maintained versions only.
| 20 | **Not maintained** (end of life: 2026-04-30) |

Images of non-maintained versions are still available but no longer receive fixes: use them at your own risk and plan your upgrade.
Critical vulnerabilities may get an exceptional fix (e.g. [php2shell](docs/security-php2shell.md)).

## Usage

Expand Down Expand Up @@ -209,6 +199,7 @@ Most features are configured with environment variables:
| [Processes](docs/processes.md) | Startup commands, cron jobs and daemons run along with the main command | `STARTUP_COMMAND_*`, `CRON_*`, `DAEMON_*` |
| [Debugging and profiling](docs/debugging.md) | Xdebug and Blackfire | `PHP_EXTENSION_XDEBUG`, `XDEBUG_CLIENT_HOST`, `BLACKFIRE_AGENT` |
| [Advanced features](docs/advanced.md) | Permissions, NodeJS, Composer version, locales, SSH keys, Kubernetes | `DOCKER_USER`, `COMPOSER_VERSION`, `LOCALES` |
| [php2shell](docs/security-php2shell.md) | What to do about CVE-2026-109618 / CVE-2026-109619 | |

The slim images install some tools during the build of your image with an ARG before the `FROM`: `PHP_EXTENSIONS`,
`INSTALL_CRON`, `INSTALL_LOCALES`, `NODE_VERSION` and `COMPOSER_VERSION`.
Expand Down
Loading
Loading