0

PHP preloading (PHP 7.4+) — preload.php and opcache.preload

Advanced5 min read·php-15-009
performance

Concept

PHP-FPM (FastCGI Process Manager) manages a pool of PHP worker processes that serve web requests. Its configuration directly impacts application performance and capacity.

How PHP-FPM works: A web server (Nginx/Apache) receives HTTP requests and passes them to PHP-FPM via FastCGI. PHP-FPM maintains a pool of worker processes. Each worker handles one request at a time, then returns to the pool. The number of workers determines concurrency — more workers means more simultaneous requests, but also more memory.

Process management modes (pm directive):

  • static: Fixed number of workers (pm.max_children). Workers are always running. Best for high-traffic servers where you want predictable memory usage and no spawn overhead.
  • dynamic: Workers spawn and die based on demand. Bounded by pm.min_spare_servers, pm.max_spare_servers, pm.max_children. Good for variable traffic.
  • ondemand: Workers start on demand and stop when idle for pm.process_idle_timeout. Best for low-traffic servers (saves memory when idle).

Key tuning knobs:

  • pm.max_children: Maximum number of workers. floor(available_memory / memory_per_worker). Measure a worker's memory with ps aux | grep php-fpm | awk '{print $6}' (RSS in KB). Typical Laravel worker: 50-100MB.
  • pm.max_requests: Workers restart after this many requests (prevents memory leaks from accumulating). 500-1000 is typical.
  • request_terminate_timeout: Kill stuck workers after N seconds.
  • listen.backlog: Queue of pending connections waiting for an available worker.

Code Example

ini
; /etc/php/8.2/fpm/pool.d/www.conf
[www]

; Socket (faster than TCP) or TCP
listen = /run/php/php8.2-fpm.sock
; listen = 127.0.0.1:9000

; Worker count: floor(available_RAM / avg_worker_RAM)
; 4GB server, 100MB per worker: floor(3500 / 100) = 35 workers
pm = dynamic
pm.max_children = 35
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500  ; restart worker after 500 requests (memory leak prevention)

; Slow request logging
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 5s  ; log requests taking more than 5 seconds

; Kill stuck requests
request_terminate_timeout = 60s

; Process title in ps output
process.user = www-data
process.group = www-data

; Environment variables for the pool
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[APP_ENV] = production
bash
# Check current PHP-FPM processes and memory usage
ps aux | grep php-fpm | awk '{sum+=$6} END {print "Total: " sum/1024 " MB"}'

# Reload config without downtime
systemctl reload php8.2-fpm

# Monitor status endpoint (enable in pool config: pm.status_path = /fpm-status)
curl http://127.0.0.1/fpm-status
# Shows: active, idle, max active, accepted connections, etc.