Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

Running PHP in Containers

PHP in a container needs the right image variant, a production configuration, sensible memory limits and a process that rebuilds images with every security release.

Last updated 5 min readPHP knowledge

In short: To run PHP in containers, choose the suitable variant of the official image (fpm, apache or cli), activate the production php.ini, install only the extensions you need and set memory and process limits that fit together. Containers are immutable: a PHP security update such as the one for PHP2Shell takes effect only when the image has been rebuilt and every container restarted. This rebuild therefore belongs in every container operation as a fixed process.

Why PHP in containers?

Containers package PHP, extensions and configuration into an image that runs the same on every server. For companies this means: fewer surprises between test and production, faster recovery and clear version states. For security, however, it also means a new duty: nobody applies updates automatically inside a container.

This page covers day-to-day operation. How to patch existing containers against PHP2Shell concretely is described under Patching PHP in Docker containers.

Choosing the right image variant

Variant Contents Typical use
php:<version>-fpm PHP-FPM, needs an upstream web server production with Nginx, Caddy or an ingress
php:<version>-apache Apache with mod_php, mpm_prefork by default simple setups, older applications
php:<version>-cli command line, the only variant that also contains php-cgi cron jobs, queue workers, build steps
…-alpine based on Alpine Linux with musl small images, adjustments needed for some extensions
…-bookworm, …-trixie based on the named Debian version fixed base for reproducible builds

For new projects, the fpm variant with a separate web server container is generally recommended. With mod_php, the PHP team sees the highest risk in connection with PHP2Shell; anyone using the apache image operates exactly this mode.

Building a production image

A lean example for an application with MySQL access:

FROM php:8.4-fpm-alpine

# Activate the production configuration (strongly recommended by the image docs)
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Only the extensions that are needed
RUN docker-php-ext-install -j"$(nproc)" pdo_mysql

# Own settings
COPY zz-sicherheit.ini "$PHP_INI_DIR/conf.d/"

# Code belongs to root; the FPM workers run as www-data and can only read it
COPY . /var/www/html

The image's helper scripts take care of building extensions: docker-php-ext-configure for options, docker-php-ext-install for building, docker-php-ext-enable for activating, for example after pecl install. If the source code is unpacked with docker-php-source, according to the docs it should be deleted again in the same layer. The file zz-sicherheit.ini could look like this:

expose_php = Off
display_errors = Off
log_errors = On
session.use_strict_mode = 1
session.cookie_httponly = 1

Two further principles make images more robust. First, the delivered image contains only what is needed at runtime; compilers, package caches and test files belong in a separate build stage. Second, the user under which PHP runs may write only where necessary, for example in upload or cache directories on mounted volumes. Many teams additionally run the container with a read-only file system. A PHP file injected into it then has a much harder time staying, and every restart restores the clean state of the image. This does not replace an investigation after an incident, but it noticeably reduces the attack surface.

Processes, memory and logs in the container

Three levers determine whether a PHP container runs stably.

  1. Number of workers: pm.max_children in the FPM pool determines how many requests are handled at the same time. Each worker can occupy up to memory_limit.
  2. Memory limit of the container: It limits all processes together. As a rough rule, the number of workers times memory per worker plus a reserve must fit under the limit, otherwise the system terminates processes.
  3. Logs: Containers write logs to standard output. In PHP-FPM, catch_workers_output = yes routes the workers' output into the main log; without the setting it ends up nowhere, according to the manual.

Also helpful is request_terminate_timeout, which ends individual requests after a period of time when max_execution_time does not take effect. How to run containers in a cluster is described in Patching PHP in Kubernetes.

Containers and security: what PHP2Shell has shown

With PHP2Shell, containers play a dual role. On the one hand they are a help: as an interim measure for CVE-2026-109618, php.net recommends combining memory_limit=-1 with a memory limit via container or cgroup, ideally in such a way that only the affected worker is terminated. According to php.net, RLIMIT_AS and RLIMIT_DATA are unsuitable for this. This measure is not complete protection and does not replace the update.

On the other hand, containers are a trap if images are not rebuilt. An image with PHP 8.4.26 remains vulnerable no matter how often the container restarts. Only a rebuild to 8.4.27 or newer and the replacement of all running containers close the gap. The state can be checked directly in the running container:

docker exec <container> php -v
docker exec <container> php-fpm -v
docker exec <container> php -r 'var_dump(ini_get("memory_hard_limit_percent"));'

If the last command returns bool(false), PHP does not know the directive introduced with the fixed versions; according to the manual, that version is then affected. In the long term only an automatic rebuild helps, as described in CI/CD: rebuilding PHP images automatically.

Tip

Define the exact PHP version in the tag in the Dockerfile and raise it deliberately. That way it is traceable in the code when which version was rolled out.

How does PHP SOS help?

As part of server hardening for PHP web servers, we also look after containers:

  • Check images: Base image, PHP version, extensions and configuration of every image.
  • Harden the configuration: Production ini, lean extension list, suitable worker and memory limits.
  • Set up rebuilds: A process that rebuilds and rolls out images on new PHP releases.
  • Verify the runtime: We demonstrate that the corrected version is running in every running container.

Frequently asked questions

Which variant of the PHP image is the right one?

For Nginx or another proxy, the fpm variant; for a simple setup with Apache, the apache variant; for scripts and cron jobs, the cli variant. Alpine variants are smaller but based on musl instead of glibc, which shows up with some extensions.

Is PHP in a container updated automatically?

No. A running container does not change. You must rebuild the image with the new PHP version and restart the containers. Only then does the corrected version run.

How do memory_limit and the container limit relate?

memory_limit limits each individual PHP request, the container limit the whole container. Both must fit together: the number of workers times memory per worker should be below the container limit.

Do I need a php.ini in containers?

Yes. The official images ship php.ini-development and php.ini-production as templates. For production, the image documentation expressly recommends the production template; your own settings go into conf.d.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help