Skip to content

Harden image for production ramps with Apache mpm_event + PHP-FPM #9

Description

@simonbuerger

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

  • Remove mod_php and enable mpm_event + proxy_fcgi.
  • Add php-fpm and Supervisord programs (apache2, php-fpm).
  • Add Apache php-fpm.conf, keepalive.conf, status.conf and enable them.
  • Add env-templated www.conf (pool) and sensible PHP defaults (opcache).
  • Implement /usr/local/bin/tune-concurrency.sh and wire into entrypoint.
  • Implement /healthz and container HEALTHCHECK.
  • Log DOCKER_SYNC_ENABLED state and fast-path mount on boot.
  • Write Sizing & Tuning doc page (envs + simple formula).
  • Publish legacy prefork tag from previous commit.
  • Cut minor release and update README.

Labels: enhancement performance infra production-hardening

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions