Getting Started

Tags and versions

How tags are named, how the exact PHP release is pinned, and how new releases are picked up automatically.

Two tags per image

Every image is published under two tags for each supported PHP minor.

TagExampleBehaviour
Minoradamrollogi/php:8.4Moves forward to the newest pinned release of that minor.
Exact releaseadamrollogi/php:8.4.26Immutable. Never republished with a different PHP build.

Use the minor tag where you want PHP security fixes to arrive on their own. Pin the exact tag in production if you want to control exactly when PHP changes, then move it deliberately.

The same scheme applies to php-nginx and php-nginx-dev. The base image has a single latest tag.

Pre-release tags

PHP 8.6 is published while it is a release candidate. Its exact tag follows Alpine's package spelling, for example php:8.6.0_rc3, and the minor tag php:8.6 tracks whatever candidate is current. When 8.6.0 ships, the minor tag moves to it automatically.

Treat 8.6 as experimental until the final release. The imagick extension is not yet packaged for it.

How a release is pinned

The exact release for each minor lives in a single file in the repository, alpine/php-versions.txt:

alpine/php-versions.txt
8.2 8.2.34
8.3 8.3.35
8.4 8.4.26
8.5 8.5.11
8.6 8.6.0_rc3

The build passes that release to the Dockerfile, which installs every PHP package, the core and each extension, with the same version constraint such as php84~8.4.26. Pinning the extensions matters: Alpine's extension packages do not constrain the core version themselves, so without the pin a new release arriving in Alpine would be installed as extensions against the older core and nothing would load. After installation the build asserts that php -v reports the pinned version and fails if it does not. A tag therefore cannot contain a different PHP than its name says, nor a mismatched set of extensions.

How updates arrive

PHP packages come from Alpine, which typically lags php.net by a few days. The images install from the base image's stable branch plus the edge repositories, and apk takes the newest version it can see across them. A scheduled job runs every four hours and:

  1. Downloads the package index of every one of those repositories for both x86_64 and aarch64.
  2. Compares each pin with the newest release available, exactly as apk would resolve it. A minor is only bumped when the same release is present on both architectures.
  3. Commits the new pin to main and dispatches the build, which publishes the new exact tag and moves the minor tag.

The same check also shows the latest release on php.net, so a security fix that Alpine has not yet packaged is visible as "waiting on Alpine" rather than hidden:

PHP 8.4: pinned 8.4.26, Alpine 8.4.26, php.net 8.4.27 (security release, waiting on Alpine)

From the moment Alpine packages a release, the worst case to a published image is roughly four hours plus the build.

Images are also rebuilt weekly even when nothing changed, so Alpine's own security fixes for musl, OpenSSL, Nginx and friends ship without waiting for a PHP release.

Checking what a tag contains

docker run --rm adamrollogi/php:8.4 php -v
docker inspect adamrollogi/php:8.4 --format '{{ index .Config.Labels "org.opencontainers.image.version" }}'

The org.opencontainers.image.version label carries the exact PHP release, and every image ships with an SBOM and provenance attestation you can inspect with docker buildx imagetools inspect.