Sign inSign up

wiediiregistry/php

By wiediiregistry

•Updated 7 days ago

Buildkit cache
Image
0

100K+

wiediiregistry/php repository overview

⁠Wiedii Base Docker Images

Who this is for: people who run PHP applications on these images (developers and DevOps). Everything you need to choose an image, start a container and add PHP extensions is on this page. If you maintain the images, read the project documentation in the repository instead.

Ready-made PHP images for Wiedii projects. A Docker image is a packaged, ready-to-run environment. These ones contain PHP, a web server, the PHP extensions most applications need and safe defaults. They run as a non-root user (an ordinary account instead of the all-powerful root administrator), use base images pinned by digest (a fingerprint of their content), and come in a production mode and a development mode. There are versions for Laravel, Symfony and any other PHP application. Every tag works on Intel/AMD (linux/amd64) and on Apple Silicon or ARM servers (linux/arm64).


⁠Pick your image

An image name is made of the PHP version, the variant and the operating system, plus -dev for the development mode:

wiediiregistry/php:<php-version>-<variant>-<debian|alpine>[-dev]

The version is always the full patch number (8.5.12, not 8.5). Examples: wiediiregistry/php:8.5.12-fpm-debian, wiediiregistry/php:8.5.12-symfony-alpine, wiediiregistry/php:8.5.12-fpm-alpine-dev. You can see every tag on this Docker Hub page, under Tags.

  • PHP versions: 8.5 (latest, for new projects), 8.4 (stable, recommended for production), 8.3 (stable LTS, for existing projects).
  • Operating system: Debian (more compatible) or Alpine (smaller).

A variant is a flavor of the image for one kind of project:

VariantWhat it isUse it forPort
fpmPHP-FPM plus the Caddy web serverGeneric web applications, APIs8080
laravelSame as fpm, set up for Laravel (adds pcntl)Laravel applications8080
symfonySame as fpm, set up for Symfony (adds pcntl)Symfony applications8080
cliPHP on the command line, no web serverQueue workers, schedulers, cronnone
bare-fpmLike fpm, on a hardened base with no known flawsWeb apps that need the smallest attack surface8080
bare-cliLike cli, on a hardened base with no known flawsWorkers that need the smallest attack surfacenone

PHP-FPM is the program that runs PHP code for a web server, and Caddy is that web server. Not sure which to pick? Use laravel, symfony or fpm for a web application and cli for background work.

⁠The bare-* variants have limits

The bare-* variants use Docker Hardened Images (DHI): minimal base images from Docker with no known security flaws (zero CVEs, where a CVE is a publicly listed vulnerability). They trade convenience for that:

  • No shell and no package manager in production. You cannot run RUN commands in your final build stage or open bash in the running container. Do the work in an earlier build stage (for example from the matching -dev image) and copy the result with COPY --from.
  • Few extensions. Bare images ship only the extensions Docker compiles into PHP (the core set, which includes intl, mbstring, sodium, opcache, ctype, iconv, xml, pdo and pdo_sqlite) and none of the ones we add on top in the standard images (redis, mongodb, gd, imagick, zip, bcmath, apcu, igbinary, gmp, calendar, mysqli, pdo_mysql). If your application needs those, choose a standard variant.
  • A different user: nonroot (UID 65532) instead of app.

⁠Production and development modes

ModeTag suffixWhat is inside
ProductionnonePHP, the web server (web variants), OPcache with JIT (keeps compiled PHP in memory), no debug tools, no Composer
Development-devEverything above plus Xdebug (the PHP debugger, port 9003), Composer (PHP's package manager), Zsh, Starship (a friendly prompt), mise (installs other runtimes such as Node), git-flow-next and GPG for signing commits. The app user has sudo (runs a command as administrator), for local development only

A -dev image is meant to be opened as a devcontainer, a ready-made workspace that VS Code or JetBrains starts inside Docker. The bare-* -dev images differ: their shell is bash (Debian) or ash (Alpine) instead of Zsh, they have no GPG and no sudo, and the only user is nonroot.


⁠Quick start

Put one of these in your project's Dockerfile, then build it with docker build -t my-app .. Each block below is a separate example, not one file.

Production, Laravel. The production image has no Composer, so it is borrowed from its official image during the build only (the @sha256 part is explained below):

FROM wiediiregistry/php:8.5.12-laravel-debian
COPY --chown=app:app . /var/www/html/
RUN --mount=type=bind,from=composer/composer:lts-bin@sha256:643030268f2b073331256d8dfaf8e935dfbb0164fc39c1d41e7355e26ad80707,source=/composer,target=/usr/bin/composer \
    composer install --no-dev --optimize-autoloader --no-interaction
RUN php artisan optimize

Production, general PHP (no build step):

FROM wiediiregistry/php:8.5.12-fpm-debian
COPY --chown=app:app . /var/www/html/

Development, Laravel (debugger, Composer, Zsh and mise included):

FROM wiediiregistry/php:8.5.12-laravel-debian-dev

Command-line worker:

FROM wiediiregistry/php:8.5.12-cli-debian
COPY --chown=app:app . /var/www/html/
CMD ["php", "artisan", "queue:work", "--sleep=3", "--tries=3"]

Check that it works. First, ask the image for its PHP version:

docker run --rm wiediiregistry/php:8.5.12-fpm-debian php -v

It prints the PHP version. Next, start the image you built from one of the examples above. The web variants listen on port 8080. This command keeps running, so leave it open:

docker run --rm -p 8080:8080 my-app

In a second terminal window, call the health check. HTTP/1.1 200 in the answer means the web server is up:

curl -i http://localhost:8080/healthz
⁠What the @sha256:… in the Composer line is
  • A tag such as lts-bin is a label. The Composer maintainers move it to a newer release whenever they publish one, so building today and next month can give you a different Composer without any change in your Dockerfile.
  • A digest is a fingerprint (a SHA256 hash) of the exact content of an image. composer/composer:lts-bin@sha256:… always means those exact bytes, and Docker refuses anything else.
  • Pinning (writing the digest next to the tag) makes your build reproducible: the same Dockerfile gives the same result next year, and a changed or tampered image cannot slip into your build unnoticed. Docker uses the digest; the tag stays so a person can read what it is.

To get the current digest:

  1. Run docker buildx imagetools inspect composer/composer:lts-bin.
  2. In the output, copy the value of the first Digest: line, the one at the top (for example sha256:6430…0707). Ignore the digests further down under Manifests:: they belong to single CPU types.
  3. Paste it after the @ in your Dockerfile.

The digest in the example is the one these images used when this text was written, so it may not be the newest. Update it on purpose, when you want a newer Composer: repeat the three steps, rebuild and run your tests. Dependency bots such as Renovate can propose the update for you if they recognize --mount … from= lines.


⁠PHP extensions

A PHP extension adds a capability to PHP, such as talking to a database. All standard variants (fpm, laravel, symfony, cli) ship these 18 extensions in production and development:

zip, sodium, mbstring, gd, xml, calendar, imagick, opcache, apcu, igbinary, gmp, intl, bcmath, redis, mongodb, mysqli, pdo, pdo_mysql.

On top of that: pcntl in laravel and symfony (Laravel Horizon and Symfony Messenger workers need it), and Xdebug in every -dev image. ctype and iconv are compiled into PHP in every image. Bare images carry only what Docker compiles in (see above).

⁠Adding another extension

Use install-php-extensions. It builds each extension from a verified source, and it never uses pecl (PHP's old extension tool, which is deprecated and had outages). The -dev images already contain the command: inside a running standard -dev container, run sudo install-php-extensions pcov (the bare-* -dev images have no sudo). In a production Dockerfile, mount the installer image for the one RUN that needs it, so none of it stays in your final image:

FROM wiediiregistry/php:8.5.12-laravel-debian
USER root
RUN --mount=type=bind,from=wiediiregistry/php-extension-installer:0.1.3@sha256:e03f134a49496945dbb72c9da46e99da82ae22db4f61355c577afaebef1f8975,source=/usr,target=/opt/wiedii-ext \
    sh /opt/wiedii-ext/bin/install-php-extensions exif pcov
USER app

0.1.3 is the installer release these images are built with, and the sha256: part is its digest (a fingerprint of the image's exact content), so your build gets exactly the same installer. For a newer release, run docker buildx imagetools inspect wiediiregistry/php-extension-installer:<release> and copy the first Digest: line. Avoid the short tags 0.1 and 0: they move to each new release. The installer has its own page on Docker Hub: wiediiregistry/php-extension-installer⁠.

What to know:

  • Names outside our tested list are looked up on Packagist⁠, the official index of PIE⁠ (the PHP Foundation's extension installer), and installed at the latest stable release. The same applies to a version you ask for, such as redis-6.3.0. That path is not reproducible (a newer release can appear tomorrow), so the command prints a warning. It is a deliberate exception: to get a reproducible build, ask the maintainers to add the extension to the tested list with a pin.

  • Bare images: no extra PHP-own extensions such as gd or zip, because there is no PHP source in them. On bare Debian, none that needs system libraries either; bare Alpine can install those.

  • Bare production images have no shell, so you cannot run the installer in them: build the extension in the matching -dev image and copy only its .so file (the compiled extension) and its .ini file (the setting that turns it on).

  • Rate limits: extensions are downloaded from GitHub, which limits anonymous requests. If a build hits the limit, give it a GitHub token as a BuildKit secret (a value that exists only while one build step runs). Never pass it as a build argument: arguments stay readable in the image history. Add --mount=type=secret,id=composer_auth to the RUN line above, put the token in a file, and pass the file to the build:

    echo '{"github-oauth": {"github.com": "<token>"}}' > auth.json
    docker build --secret id=composer_auth,src=auth.json .
    

    Replace <token> with a GitHub token you created, and never commit auth.json.


⁠Users and security

  • A non-root user: app in every standard image and nonroot in every bare-* image. Linux identifies each user by a number, the UID (user ID), and each group by a GID (group ID): app is UID/GID 1001 and nonroot is UID/GID 65532. In production images no process runs as root, so a compromised application does not get administrator rights in the container. If a mounted folder is not writable, make the host folder match that UID/GID. app belongs to its own group only: it is no longer in the www-data group (GID 33 on Debian, 82 on Alpine), so a project that relied on www-data group permissions must switch to app:app.
  • sudo in -dev images only: a -dev image is a local development workspace, and the maintainers allow developers to add tools inside it without rebuilding (mise use, sudo apt-get install, sudo apk add, sudo install-php-extensions). For that, app has passwordless sudo there; the bare-* -dev images have none. It is a deliberate exception, approved by the tech lead on 2026-10-25; the maintainers will review it by 2027-04-24. Production images have no sudo. Never run a -dev image in production.
  • Pinned software: base images and every tool downloaded during the build are fixed to exact versions and checksums, so the same tag always contains the same software.
  • Hardened PHP and web server: expose_php=Off, safe sessions, only .php files executed. Caddy sends security headers (HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy and others) and blocks sensitive paths (.env, .git, vendor, storage/logs). Caddy compresses responses with Brotli, zstd and gzip.
  • A GitHub token for Composer: the standard images and the -dev images of the bare-* variants contain a GitHub token in Composer's configuration (auth.json), so composer install is not throttled by GitHub's limit for anonymous requests (60 per hour per IP address). This helps when your own Dockerfile installs packages in a build stage that starts from one of these images, and when you run Composer inside a -dev container. It belongs to a dedicated company account that can only read public repositories, so it gives no access to anything private. It is kept on purpose. Anyone who pulls the image can read it, and security scanners such as Docker Scout or Trivy may report it; that finding is expected. The bare-* production images do not contain it.
  • Ports: web variants listen on 8080 (GET /healthz answers 200 when the web server is up). PHP-FPM listens on 9000 inside the container only; do not publish it. The cli and bare-cli variants open no port.

⁠Troubleshooting

  • no match for platform: the tag has no image for your CPU type. Check that the tag lists both linux/amd64 and linux/arm64: docker buildx imagetools inspect wiediiregistry/php:<tag>.
  • A file is not writable in the container: the container user is app (UID 1001), or nonroot (UID 65532) on bare images. Make the host folder match.
  • Looking inside a running container: docker exec -it <container> bash opens a shell (not on bare-* production images, where docker debug <container> works instead; it comes with Docker Desktop and is missing from some Docker installations, so otherwise run the matching -dev image), and docker logs <container> shows its output.

⁠More help

The project repository has longer guides (variants, tags, the production and development environments, security, every extension by variant) in English and Spanish, in its docs/ folder. It is internal to Wiedii, so this page repeats everything a visitor needs. General references: Docker documentation⁠, PHP manual⁠, Caddy documentation⁠.


⁠License

UNLICENSED. Internal Wiedii use only.

Tag summary

Content type

Image

Digest

sha256:2320628cc…

Size

396.4 MB

Last updated

7 days ago

docker pull wiediiregistry/php:8.5.12-symfony-alpine-dev-6f8cf4bcf5ed