Objective
- Standardise on a single, scalable runtime: Apache
mpm_event + PHP-FPM.
- Keep defaults safe and self-tuning; expose a few env knobs for advanced users.
- Retain
DOCKER_SYNC_ENABLED fast-path. Avoid dual prefork/FPM maintenance.
Implementation plan (small, focused changes)
Dockerfile
- Install php-fpm (same PHP version as CLI).
- Enable Apache modules:
mpm_event, proxy_fcgi, setenvif, rewrite, deflate, headers, http2 (optional), status.
- Remove
mod_php; route .php to FPM via unix socket.
Supervisord
- Programs:
apache2, php-fpm
autorestart=unexpected
stopasgroup=true
Apache config (conf-available)
php-fpm.conf
# Route all .php to PHP-FPM over a unix socket
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php-fpm.sock|fcgi://localhost/"
</FilesMatch>
# Backend timeout for FPM
ProxyTimeout ${APACHE_PROXY_TIMEOUT:-120}
keepalive.conf
KeepAlive On
MaxKeepAliveRequests ${APACHE_MAX_KEEPALIVE_REQUESTS:-100}
KeepAliveTimeout ${APACHE_KEEPALIVE_TIMEOUT:-2}
status.conf (local only)
<Location /server-status>
SetHandler server-status
Require local
</Location>
PHP-FPM pool defaults (env-templated)
; www.conf template
pm = ${PHP_FPM_PM:-dynamic}
; auto-tuned unless PHP_FPM_MAX_CHILDREN is set
pm.max_children = ${PHP_FPM_MAX_CHILDREN:-auto}
pm.start_servers = ${PHP_FPM_START_SERVERS:-10}
pm.min_spare_servers = ${PHP_FPM_MIN_SPARE_SERVERS:-10}
pm.max_spare_servers = ${PHP_FPM_MAX_SPARE_SERVERS:-20}
pm.max_requests = ${PHP_FPM_MAX_REQUESTS:-1000}
request_terminate_timeout = ${PHP_FPM_REQ_TMO:-120}
; Slowlog
slowlog = /home/LogFiles/php-fpm.slow.log
request_slowlog_timeout = ${PHP_FPM_SLOWLOG:-0}
; Health
ping.path = /ping
pm.status_path = /status
Auto-tuning script (simple, no deps) — /usr/local/bin/tune-concurrency.sh
- Read memory limit (
/sys/fs/cgroup/memory.max for cgroup v2; fall back to /proc/meminfo MemTotal).
- Reserve headroom =
max(1024 MB, 15% of limit).
- Assume child RSS =
${PHP_CHILD_MB:-48}.
- If
PHP_FPM_MAX_CHILDREN unset:
pm.max_children = clamp(floor((limitMB − headroomMB)/childMB), 50, 800)
- Echo chosen values on startup.
Entry / boot
- Call tuner before starting services.
- Log
DOCKER_SYNC_ENABLED state and “serving from "/homelive"” when enabled.
Health
HEALTHCHECK hitting /healthz (serve 200 when php-fpm ping OK).
- Document using
/healthz for App Service Health check.
Sensible PHP defaults
opcache.enable=1
opcache.memory_consumption=${PHP_OPCACHE_MB:-192}
opcache.max_accelerated_files=${PHP_OPCACHE_MAX_FILES:-100000}
opcache.revalidate_freq=${PHP_OPCACHE_REVALIDATE_SEC:-2}
Env surface (keep it short)
DOCKER_SYNC_ENABLED=0|1
PHP_FPM_MAX_CHILDREN, PHP_CHILD_MB, PHP_FPM_MAX_REQUESTS
APACHE_KEEPALIVE_TIMEOUT, APACHE_MAX_KEEPALIVE_REQUESTS, APACHE_PROXY_TIMEOUT
PHP_OPCACHE_MB, PHP_OPCACHE_MAX_FILES, PHP_OPCACHE_REVALIDATE_SEC
Acceptance criteria
- Default build starts with event + FPM; no
mod_php present.
- On first boot, logs show computed
pm.max_children and memory limit.
KeepAliveTimeout defaults to 2s; pm.max_requests defaults to 1000.
/healthz returns 200; /ping and /status work locally.
- With
DOCKER_SYNC_ENABLED=1, logs confirm fast-path active (“serving from "/homelive"”).
- Under synthetic latency (slowlog proves >2–3s requests), no immediate 5xx burst; FPM queue increases before front-end timeouts.
- Docs: 1-page “Sizing & Tuning” with the envs and a two-line formula.
Migration note
- Make this a minor / feature release; no breaking change for typical users.
- For anyone who must keep prefork, provide a legacy tag (built from previous commit) rather than maintaining two code paths.
Rationale (one line)
event + FPM yields more headroom under keep-alive and transient latency, while keeping the image simple and configurable with a few well-chosen environment variables.
Tasks
Labels: enhancement performance infra production-hardening
Implementation plan (small, focused changes)
Dockerfile
mpm_event,proxy_fcgi,setenvif,rewrite,deflate,headers,http2(optional),status.mod_php; route.phpto FPM via unix socket.Supervisord
apache2,php-fpmautorestart=unexpectedstopasgroup=trueApache config (
conf-available)php-fpm.confkeepalive.confstatus.conf(local only)PHP-FPM pool defaults (env-templated)
Auto-tuning script (simple, no deps) —
/usr/local/bin/tune-concurrency.sh/sys/fs/cgroup/memory.maxfor cgroup v2; fall back to/proc/meminfoMemTotal).max(1024 MB, 15% of limit).${PHP_CHILD_MB:-48}.PHP_FPM_MAX_CHILDRENunset:pm.max_children = clamp(floor((limitMB − headroomMB)/childMB), 50, 800)Entry / boot
DOCKER_SYNC_ENABLEDstate and “serving from "/homelive"” when enabled.Health
HEALTHCHECKhitting/healthz(serve 200 when php-fpm ping OK)./healthzfor App Service Health check.Sensible PHP defaults
Env surface (keep it short)
DOCKER_SYNC_ENABLED=0|1PHP_FPM_MAX_CHILDREN,PHP_CHILD_MB,PHP_FPM_MAX_REQUESTSAPACHE_KEEPALIVE_TIMEOUT,APACHE_MAX_KEEPALIVE_REQUESTS,APACHE_PROXY_TIMEOUTPHP_OPCACHE_MB,PHP_OPCACHE_MAX_FILES,PHP_OPCACHE_REVALIDATE_SECAcceptance criteria
mod_phppresent.pm.max_childrenand memory limit.KeepAliveTimeoutdefaults to 2s;pm.max_requestsdefaults to 1000./healthzreturns 200;/pingand/statuswork locally.DOCKER_SYNC_ENABLED=1, logs confirm fast-path active (“serving from "/homelive"”).Migration note
Rationale (one line)
event + FPM yields more headroom under keep-alive and transient latency, while keeping the image simple and configurable with a few well-chosen environment variables.
Tasks
mod_phpand enablempm_event+proxy_fcgi.php-fpmand Supervisord programs (apache2,php-fpm).php-fpm.conf,keepalive.conf,status.confand enable them.www.conf(pool) and sensible PHP defaults (opcache)./usr/local/bin/tune-concurrency.shand wire into entrypoint./healthzand containerHEALTHCHECK.DOCKER_SYNC_ENABLEDstate and fast-path mount on boot.Labels:
enhancementperformanceinfraproduction-hardening