PHP-FPM Image
Complete web stack with separate PHP-FPM and Nginx images, for web apps, REST APIs and SPAs. Security headers, gzip compression, static asset caching, FPM pool tuning. All configurable via ENV.
Overview
The project provides two companion images: headgent/phpfpm (PHP-FPM with a complete extension stack) and headgent/nginx (pre-configured reverse proxy with security hardening). Both are multi-arch, multi-stage and configurable via environment variables without rebuilding.
GitHub: jardisOps/phpfpm
| Image | Tag format | Size |
|---|---|---|
headgent/phpfpm | 8.2, 8.3, 8.4, latest | ~130 MB |
headgent/nginx | 1.28 | ~25 MB |
| Property | Value |
|---|---|
| PHP versions | 8.2, 8.3, 8.4 |
| Nginx version | 1.28 |
| Architectures | linux/amd64, linux/arm64 |
| Base | Alpine 3.23 |
Installation
docker pull headgent/phpfpm:8.4
docker pull headgent/nginx:1.28Quick Start
# docker-compose.yml
services:
app:
image: headgent/phpfpm:8.4
volumes:
- ./:/app
environment:
PHP_MEMORY_LIMIT: 256M
XDEBUG_MODE: "off"
FPM_PM_MAX_CHILDREN: 10
webserver:
image: headgent/nginx:1.28
ports:
- "80:80"
environment:
APP_ROOT: /app
DOCUMENT_ROOT: /public
INDEX_FILE: index.php
PHP_PORT: 9000
depends_on:
- appPHP-FPM Image
Extensions
The PHP-FPM image includes the same extension stack as the CLI image, except for pcntl (not relevant for FPM processes):
Database: pdo, pdo_mysql, mysqli, pdo_pgsql Messaging: amqp (2.2.0), rdkafka (6.0.5) Caching: apcu (5.1.28), redis (6.3.0), opcache Web/I/O: curl, soap, sockets, dom, zip Text: mbstring, intl, bcmath Media: gd (FreeType + JPEG), exif Debugging: xdebug (3.5.1), pcov (1.0.12)
PHP Configuration
Identical to the CLI image, with these differences:
| Variable | CLI Default | FPM Default |
|---|---|---|
PHP_MAX_EXECUTION_TIME | 0 (unlimited) | 30 (seconds) |
XDEBUG_MODE | debug | off |
OPCACHE_VALIDATE_TIMESTAMPS | — | 0 (production) |
OPCACHE_REVALIDATE_FREQ | 2 | 0 (production) |
FPM Pool Configuration
| Variable | Default | Description |
|---|---|---|
FPM_PM | dynamic | Process manager (static, dynamic, ondemand) |
FPM_PM_MAX_CHILDREN | 5 | Max worker processes |
FPM_PM_START_SERVERS | 2 | Initial workers |
FPM_PM_MIN_SPARE_SERVERS | 1 | Min idle workers |
FPM_PM_MAX_SPARE_SERVERS | 3 | Max idle workers |
FPM_PM_MAX_REQUESTS | 500 | Requests per worker before restart |
Pool Tuning for Production
For production loads: FPM_PM=static with FPM_PM_MAX_CHILDREN = available RAM ÷ ~40 MB per worker. A server with 4 GB RAM → FPM_PM_MAX_CHILDREN=80 (minus OS + Nginx + Redis).
Health Check
The image has a built-in health check via the FPM ping endpoint:
cgi-fcgi -bind -connect 127.0.0.1:9000 /ping → "pong"Status endpoint: /status (FPM pool statistics).
Nginx Image
Features
- Gzip compression — level 6, for JS, JSON, XML, CSS, text, SVG
- Security headers — HSTS, CSP, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection
- Static asset caching — 1 year for CSS, JS, images, fonts, icons
- FastCGI optimization — 128k buffer, 600s timeout, correct PATH_INFO handling
- Real IP support — Docker networks and private networks
- Security — blocks
.htaccess,.svn,.git,.env
Configuration
| Variable | Default | Description |
|---|---|---|
APP_ROOT | /app | Application base directory |
DOCUMENT_ROOT | /public | Relative document root |
INDEX_FILE | index.php | Front controller |
HOST | localhost | Server name |
PHP_PORT | 9000 | PHP-FPM port |
The Nginx configuration is generated at startup via envsubst from a template. Variables are substituted at runtime.
Security Headers
Referrer-Policy: no-referrer-when-downgrade
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'Blocked Paths
/.htaccess → 403
/.svn → 403
/.git → 403
/.env → 403Security
- Non-root execution — both images run as
appuser(configurable via PUID/PGID) - Automatic UID/GID adaptation — FPM image adapts to volume owner
- No Xdebug in production — default
XDEBUG_MODE=off expose_php = Off— PHP version not exposed- Server tokens disabled — Nginx version not exposed
- Health check — FPM ping on port 9000
Makefile
| Target | Description |
|---|---|
make phpfpm-build | Build PHP-FPM locally |
make phpfpm-build-all | Build all PHP versions locally |
make nginx-build | Build Nginx locally |
make build-all | Build all images locally |
make phpfpm-push | Build PHP-FPM multi-arch and push |
make phpfpm-push-all | Push all PHP versions multi-arch |
make nginx-push | Push Nginx multi-arch |
make build-and-push-all | Build and push everything |
make build-cache-delete | Delete buildx cache |
make info | Show build configuration |
Use Cases
- Web frameworks — Laravel, Symfony, Slim, custom
- REST APIs & GraphQL — JSON endpoints with FastCGI optimization
- Single-page apps — static asset serving with 1-year cache
- E-commerce & SaaS — production stack with pool tuning
- WordPress & CMS — standard PHP applications