Skip to content

Add an optional built-in Apache to the fpm variant (PHP_FPM_WEB_SERVER=apache) - #418

Open
mistraloz wants to merge 8 commits into
thecodingmachine:v5from
mistraloz:feat/fpm-builtin-apache
Open

mistraloz wants to merge 8 commits into
thecodingmachine:v5from
mistraloz:feat/fpm-builtin-apache

Conversation

@mistraloz

@mistraloz mistraloz commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR implements :

  • Bug
  • Feature
  • Breaking changes

The fpm variant can now run Apache in front of PHP-FPM, in the same container, with PHP_FPM_WEB_SERVER=apache. It keeps the Apache features of the apache variant (.htaccess, APACHE_DOCUMENT_ROOT, APACHE_EXTENSION_*), but PHP runs in PHP-FPM instead of mod_php.

Under the same load, the container serves 2 to 3 times more traffic with 4 times less memory (30 vs 10 pages/s on 2 CPUs, ~90 vs ~380 MiB, see benchmarks/fpm-apache). mod_php forces mpm_prefork, where every connection, idle keep-alive ones included, holds a process embedding PHP; with PHP-FPM, Apache uses mpm_event and only PHP requests reach the workers. Being an option of the fpm variant rather than a new one, it adds no tag to build, and projects using the apache variant can migrate without rewriting their .htaccess rules.

Along with it:

  • Add support for overriding PHP-FPM process manager settings #410: the PHP-FPM process manager can be configured (PHP_FPM_PM*), plus PHP_FPM_ACCESS_LOG and APACHE_PROXY_TIMEOUT; a php-fpm-healthcheck command is available for Docker and Kubernetes.
  • How to use the FPM image for serving  #195: a new docs/fpm.md page explains how to serve HTTP with the fpm image (built-in Apache or nginx), the constraints of PHP-FPM (number of workers and memory_limit, timeouts and set_time_limit(), php_value in .htaccess) and how to migrate. The README announcement links to it.
  • ALERT: [pool www] user has not been defined #206: PHP-FPM no longer fails to start when the working directory belongs to root (--tmpfs, emptyDir...): its master runs as root and its workers with the Apache user.
  • Switching the Apache MPM with APACHE_EXTENSION_* now works (the MPM is disabled last), and http2 / proxy_http2 are accepted.

Closes #410
Closes #206
Closes #195

Test plan (required)

variant-fpm.sh covers the built-in Apache (configuration, .htaccess, headers, timeouts, healthcheck, stops and crashes) and the start with a root-owned working directory; variant-apache.sh covers the MPM switch. They pass on PHP 8.4:

TAG_PREFIX=pr-fpm- docker buildx bake --load php84-slim-fpm php84-fpm php84-slim-apache php84-apache
TAG_PREFIX=pr-fpm- PHP_VERSION=8.4 BRANCH=v5 VARIANT=fpm ./tests-suite/bash_unit -f tap ./tests-suite/variant-fpm.sh

The benchmark can be run with benchmarks/fpm-apache/run.sh 8.4.

Checklist

  • I followed the guidelines in CONTRIBUTING guide
  • I have squashed any insignificant commits
  • This change has comments for package types, values, functions, and non-obvious lines of code

Modules were enabled before the others were disabled: a2enmod refuses to enable
an MPM while another one is still enabled. Disabling first also no longer skips
the second command when the first one fails.
They were listed as available in the README but silently ignored
(APACHE_EXTENSION_*) or rejected (APACHE_EXTENSIONS). HTTP/2 is not served with
mpm_prefork (required by mod_php): they are useful with the built-in Apache of
the fpm variant (mpm_event).
…R=apache)

The fpm variant can run Apache (mpm_event + proxy_fcgi) in front of PHP-FPM in
the same container: same Apache features as the apache variant (.htaccess,
APACHE_DOCUMENT_ROOT, APACHE_EXTENSION_*) without mod_php, which forces
mpm_prefork (one process embedding PHP per connection).

- Disabled by default: the fpm variant keeps its behavior (PHP-FPM on port 9000)
- apache2-fpm-foreground runs both processes; if one of them stops, the other
  one is stopped too and the container exits with an error
- PHP-FPM runs with the Apache user, the Authorization header is forwarded,
  missing PHP files are answered by Apache (404)
- PHP-FPM process manager and access log configurable with PHP_FPM_PM* and
  PHP_FPM_ACCESS_LOG (closes thecodingmachine#410)
- php-fpm-healthcheck command (ping endpoint) for Docker healthchecks and
  Kubernetes probes
@mistraloz mistraloz added the enhancement Work on new feature (or any question related to new feature) label Oct 9, 2026
- README: announcement, migration notes from the apache variant, PHP-FPM settings
- benchmarks/fpm-apache: k6 benchmark comparing the apache variant (mod_php) and
  the fpm variant with its built-in Apache under the same load (constant arrival
  rate), with the results
…root

When the working directory belongs to root (--tmpfs, Kubernetes emptyDir, some
CI runners), the commands are run as root: PHP-FPM refused to start
("[pool www] user has not been defined"), the user directives of www.conf being
commented out. With the built-in Apache, PHP-FPM was run with the Apache user
and could not open its error log (/proc/self/fd/2 belongs to the user of the
commands): "failed to open error_log: Permission denied".

The PHP-FPM master process now runs as root in these cases (like Apache), and
its workers run with the Apache user (docker by default): a pool configuration
setting user and group is written at startup, and removed otherwise (FPM logs a
notice when the user directive is set but the master is not root).

Fixes thecodingmachine#206
…riant

How to serve HTTP with the fpm image (built-in Apache, or another web server
such as nginx), the advantages over the apache variant, the constraints of
PHP-FPM (number of workers and memory, web server timeouts, php_value in
.htaccess) and the migration from the apache variant.

The README announcement and the built-in Apache section link to this page.

Closes thecodingmachine#195
…EOUT)

With the built-in Apache of the fpm variant, a PHP request that does not
answer within the Apache timeout (300 seconds) gets a 504 error, even with
set_time_limit(0). APACHE_PROXY_TIMEOUT sets ProxyTimeout (300 by default).
Absolute links pointed to the thecodingmachine repository, even when the README is read on a fork.
@mistraloz
mistraloz force-pushed the feat/fpm-builtin-apache branch from dc62c82 to 1c12a53 Compare October 9, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Work on new feature (or any question related to new feature)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for overriding PHP-FPM process manager settings ALERT: [pool www] user has not been defined How to use the FPM image for serving

1 participant