# Running PHP in Containers

> Running PHP in containers: image variants, production php.ini, extensions, memory limits, logs and updates. With examples and the link to PHP2Shell.

Source: https://phpsos.de/en/php/php-in-containern/  
As of: 25/10/2026  
Provider: PHP SOS, PHP emergency help and server security. Immediate help via WhatsApp: https://wa.me/491736494914?text=Hello%20PHP%20SOS%2C%20we%20need%20help%20with%20our%20PHP%20server.%20Company%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**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](https://phpsos.de/en/php2shell/docker-container-patchen/).

## 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:

```dockerfile
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:

```ini
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](https://phpsos.de/en/server/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:

```bash
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](https://phpsos.de/en/blog/php-images-in-ci-neu-bauen/).

> **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](https://phpsos.de/en/leistungen/server-haertung/), 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

- [Docker Official Image: PHP](https://hub.docker.com/_/php)
- [PHP Manual: FPM configuration](https://www.php.net/manual/en/install.fpm.configuration.php)
- [php.net: Security announcement of 21 October 2026](https://www.php.net/)
