PHP compatibility and project lifecycle
PHP-FPM is part of PHP and documented in the PHP manual. The example targets PHP 8.4 using the documented php:<version>-fpm tag pattern. Whether that exact tag exists is unverified from the supplied sources. Nginx image tags, licenses, support windows and performance are unknown.
Official sources checked 2026-10-22. Browser lessons on this site run PHP 8.5.
What PHP-FPM is
The PHP manual describes FPM (FastCGI Process Manager) as a primary PHP FastCGI implementation with features that are mostly useful for heavily loaded sites. It is not a separate language and not a standalone web server. It is one way of running PHP itself, as a pool of worker processes that a web server talks to over the FastCGI protocol.
That gives it a clear classification: a FastCGI process manager that ships as part of PHP. It is not an async library, a framework adapter or a historical alternative interpreter, and none of those count as standalone PHP runtimes either. PHP core is also a different thing from a CMS such as Drupal, and a framework's components are different from a CMS product. Drupal or a framework runs on top of PHP-FPM when you choose it. The supplied sources do not cover Drupal or any framework, so this guide makes no setup claims about them.
Architecture
The manual lists these capabilities:
- Advanced process management with graceful stop and start.
- Pools, which can run workers with different uid, gid, chroot, environment, listening address and php.ini.
- Configurable stdout and stderr logging.
- Emergency restart if the opcode cache is accidentally destroyed.
- Accelerated upload support.
- A slowlog that records PHP backtraces for unusually slow scripts.
- fastcgi_finish_request(), which finishes a response and flushes output while work continues.
- Dynamic, ondemand or static child spawning.
- Basic and extended status information, including json, xml and openmetrics formats.
- A php.ini-style configuration file.
In a typical deployment a reverse proxy such as Nginx or Apache receives the HTTP request. It forwards PHP requests to an FPM pool over FastCGI, and a worker runs the script and returns the response. The Docker Official Image page says an FPM image variant needs this kind of proxy in front of it.
Execution model
PHP-FPM follows the classic request lifetime. A worker takes one request, runs the script for that request, and then becomes free for the next one. The supplied sources do not describe PHP's per-request cleanup in detail, so treat that as general PHP background rather than something these sources prove.
A persistent-worker server is a different model. Examples include Swoole, Open Swoole (a separate project, not an alias for Swoole) and framework adapters built on long-running processes. In those models, application memory can survive across requests. Nothing in the supplied packets covers them, so their specifics are unknown here.
The key lesson is state leakage. In a persistent worker, static properties, global variables, singletons, in-memory caches, open connections and authenticated user objects can all carry over from one request to the next. Cleanup means a few things. Reset or rebuild per-request state at the start or end of every request. Avoid request data in globals and singletons. Close or validate connections. Recycle workers on a schedule. Test with two different users in sequence.
PHP-FPM is not that model, but process-level components can still outlive a single request. The manual mentions the opcode cache, and workers can also hold long-lived resources depending on configuration. Know what survives in your own setup.
Practical use cases
PHP-FPM suits conventional PHP web applications behind Nginx, Apache or another FastCGI-speaking proxy. It also suits setups that want separate pools with different users or ini settings, and sites that want built-in status information and a slowlog. It fits applications that want to finish a response early and continue background-style work with fastcgi_finish_request().
Installation approach
The Docker Official Image page documents a php:-fpm variant containing FPM, plus -cli, -apache and -alpine variants. The page's own examples use 8.2 and generic 8 tags. The tag list it shows is trimmed, so I cannot confirm from the supplied sources that a 8.4 FPM tag exists. The example here uses the documented pattern with 8.4, and the integration check must confirm that the tag pulls. The sources do not cover Nginx tags either, so the example leaves the Nginx image unpinned. Pin it after review.
The image ships php.ini-development and php.ini-production, and the page strongly recommends the production file for production images. Custom FPM settings go in their own .conf file under /usr/local/etc/php-fpm.d/. Files load in glob order and later files override earlier ones. The image's zz-docker.conf only contains daemonize = no and should not be overridden. The example names its file so that it sorts after www.conf and before zz-docker.conf.
Additional extensions use the image helper scripts docker-php-ext-configure, docker-php-ext-install and docker-php-ext-enable, or PECL. The page recommends explicit PECL version numbers. It also notes that installing Debian's PHP packages inside this image is blocked, because that would create two conflicting PHP installations.
Security: keep FastCGI private
The manual warns that php-fpm must not be reachable from an untrusted network. A client that can open a FastCGI connection controls the configuration used for the request, including auto_prepend_file, and can therefore execute arbitrary code. The manual says to restrict access with the listen and listen.allowed_clients directives. The Docker page adds that the FastCGI protocol is inherently trusting, and it advises against Docker's publish flag with the FPM variant. In the example, only Nginx publishes a port, and it is bound to 127.0.0.1. The php service publishes nothing.
Strengths
- It is documented by PHP itself and has a Docker Official Image variant.
- It has pools, several child-spawning modes, status output, a slowlog and graceful process handling.
- It keeps the classic request lifetime, so state does not normally accumulate in application code across requests.
- It works with any proxy that speaks FastCGI.
Limitations
- It needs a separate web server or proxy.
- Exposing FastCGI beyond a private network is dangerous.
- It does not provide an async or persistent-worker programming model.
- The supplied sources give no benchmark data, so this guide makes no performance claims.
- Exact PHP version support, license terms and support windows are unknown from the packets. The Docker page only points to separate license information.
Deployment notes
Use a private network between proxy and FPM. Start from the production ini file, consider OPcache as the Docker page suggests, and put FPM settings in a dedicated file. To run as another user, the Docker page documents the --user flag for FPM variants. The Alpine variant uses musl libc, which the page says can cause compatibility problems for some software. Provisioning hosts or cloud resources is outside this guide.
The learning track stays on PHP 8.4, with PHP 8.5 planned. This is a runtime example, so it is not a browser exercise. It needs Docker, Compose and network access to pull images.
Practical examples
These examples have not been run in a live environment yet. Commands and versions follow the official documentation linked on this page; check them before relying on them, and report a problem if one does not work.
Nginx with PHP 8.4 FPM on a private Compose network
A minimal Docker Compose stack in which Nginx is the only service published to the host, bound to 127.0.0.1:8080. The php service runs the php:8.4-fpm image, exposes FastCGI port 9000 only to the Compose network, and mounts a small script that reports its SAPI and a per-request counter. Not a browser exercise, and not yet executed.
compose.yaml
services:
web:
image: nginx
ports:
- "127.0.0.1:8080:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./public:/var/www/html:ro
depends_on:
- php
php:
image: php:8.4-fpm
# No ports entry on purpose: FastCGI must stay on the private Compose network.
expose:
- "9000"
volumes:
- ./public:/var/www/html:ro
- ./fpm/yy-app.conf:/usr/local/etc/php-fpm.d/yy-app.conf:ro
fpm/yy-app.conf
[www]
listen = 9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
nginx/default.conf
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php;
location / {
try_files $uri /index.php$is_args$args;
}
location = /index.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php:9000;
}
location ~ \.php$ {
return 404;
}
}
public/index.php
declare(strict_types=1);
function requestCounter(): int
{
static $count = 0;
$count++;
return $count;
}
requestCounter();
header('Content-Type: application/json');
echo json_encode([
'sapi' => PHP_SAPI,
'php_version' => PHP_VERSION,
'worker_pid' => getmypid(),
'counter_in_this_request' => requestCounter(),
], JSON_PRETTY_PRINT) . "\n";
Install and run
docker compose config
docker compose up -d
docker compose ps
curl http://127.0.0.1:8080/
curl http://127.0.0.1:8080/
docker compose logs php
docker compose downExpected behavior
Once the images pull, curl returns JSON with sapi set to fpm-fcgi, the PHP 8.4 version string, a worker process id, and counter_in_this_request equal to 2 on every call. The counter restarts for each request, which shows the classic request lifetime. The worker pid may differ between calls. Only 127.0.0.1:8080 appears as a published port, and port 9000 is not published to the host.
Requires Docker with Compose and network access to pull images. Not a browser exercise. The php:8.4-fpm tag follows the documented pattern but is unverified here. The Nginx image is unpinned and should be pinned after review. The listen = 9000 setting and fastcgi_params path assume the images' defaults, so confirm them in integration checks. There is no TLS, no production php.ini, no OPcache tuning and no application framework.