Security

How images are built

You're about to run our images in production. You should be able to see exactly how they're made. Every image is built in public, tested before anyone can pull it, and signed so you can prove where it came from.

Built in public, never on a laptop

Every published image comes from GitHub Actions in this repository. Images are never built or pushed from a local machine. The logs of every run are public.

The workflows in .github/workflows/ are the full recipe. This page is the short version.

The same PHP as the official images

We compile PHP from the php.net source with the same Dockerfiles that build the official images. The build checks php.net's checksum and GPG signature, exactly as upstream does.

We make three small changes. We set the PHP patch to build, skip a PECL step so a PECL outage can't fail the build, and name Server Side Up in php -v. scripts/prepare-php-base.sh shows every change.

Security fixes don't wait

  • New PHP patches: we check php.net every hour. A new patch starts a build right away.
  • Operating system fixes: every image rebuilds each Tuesday. PHP is recompiled from scratch, never from a cache, so a patched library such as OpenSSL lands in that week's images.
TriggerTags it updates
A new PHP patch on php.netFloating tags
Every Tuesday at 08:00 UTCFloating tags
A GitHub releaseFloating tags, plus pinned -vX.Y.Z tags
A GitHub pre-release, or every Monday at 08:25 UTCbeta- tags

The upgrade guide explains floating and pinned tags.

Nothing ships until every image passes

Each image is tested on native amd64 and arm64 hardware before it's published. If a single image fails to build or test, nothing from that run is published.

The image you pull is byte for byte the image the tests ran. After publishing, the pipeline checks that the digest on Docker Hub and GitHub Packages matches the tested one.

Signed, with an SBOM and provenance

Every image carries an SPDX SBOM and SLSA build provenance. Each one is signed with cosign keyless signing, tied to our publishing workflow. There's no private key that could leak.

Check them yourself before you deploy. Verifying images has the commands. Published images are also scanned for vulnerabilities.

Every dependency is pinned and reviewed

Every pinned dependency version lives in one file: php-versions-base-config.yml. Renovate proposes each update as a pull request. It waits three days after a release before it does. A maintainer reviews each one, except the weekly docker-library/php bump, which merges once every check passes. Every GitHub Action is pinned to a commit SHA.

Every change is tested the same way

A pull request that changes an image builds every variation and runs the same tests. The results are public on the pull request. Contributing shows how to run the same build and tests on your own machine.