Introduction

PHP-FPM manages a pool of worker processes that handle PHP requests. When all workers are busy processing slow requests and new requests arrive, the listen queue fills up. Once listen.backlog is also full, Nginx cannot connect to PHP-FPM and returns 502 Bad Gateway. This is a common production issue during traffic spikes or when slow database queries block workers.

Symptoms

  • 502 Bad Gateway from Nginx during high traffic
  • PHP-FPM error log: server reached pm.max_children setting, consider raising it
  • Nginx error log: connect() to unix:/run/php/php-fpm.sock failed (11: Resource temporarily unavailable)
  • Response times spike before 502 errors appear
  • pm.active_processes equals pm.max_children

``` [15-Jan-2024 10:30:00] WARNING: [pool www] server reached pm.max_children setting (50), consider raising it

[15-Jan-2024 10:30:01] ERROR: unable to read what child say: Bad file descriptor [15-Jan-2024 10:30:01] ERROR: FPM initialization failed

# Nginx error log: 2024/01/15 10:30:01 [error] 1234#0: *5678 connect() to unix:/run/php/php8.2-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream ```

Common Causes

  • pm.max_children too low for traffic volume
  • Slow requests (database queries, API calls) blocking workers
  • Memory limits preventing more workers from spawning
  • Dynamic PM not scaling fast enough for traffic spikes
  • PHP-FPM listen queue too small

Step-by-Step Fix

  1. 1.Check current pool status and configure max_children:
  2. 2.```bash
  3. 3.# Check current PHP-FPM status
  4. 4.systemctl status php8.2-fpm

# Check max children reached in logs grep "max_children" /var/log/php8.2-fpm.log

# Calculate appropriate max_children: # max_children = available RAM / memory per process # Typical PHP-FPM process: 50-100MB # With 4GB RAM reserved for PHP-FPM: 4096 / 80 = ~50

# Edit pool config # /etc/php/8.2/fpm/pool.d/www.conf pm = dynamic pm.max_children = 100 # Increase from 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 # Recycle processes after 500 requests

# Restart sudo systemctl restart php8.2-fpm ```

  1. 1.Configure listen backlog:
  2. 2.```ini
  3. 3.# /etc/php/8.2/fpm/pool.d/www.conf

# Use TCP instead of socket for better performance under load listen = 127.0.0.1:9000 # Instead of: listen = /run/php/php8.2-fpm.sock

# Increase listen backlog listen.backlog = 4096

# Nginx upstream config: # upstream php { # server 127.0.0.1:9000; # keepalive 32; # } ```

  1. 1.Enable PHP-FPM status page for monitoring:
  2. 2.```ini
  3. 3.# /etc/php/8.2/fpm/pool.d/www.conf
  4. 4.pm.status_path = /fpm-status

# Nginx config for status endpoint location = /fpm-status { allow 127.0.0.1; deny all; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

# Check status curl http://127.0.0.1/fpm-status # Output: # pool: www # process manager: dynamic # start time: 15/Jan/2024:08:00:00 +0000 # start since: 9000 # accepted conn: 123456 # listen queue: 0 # Should be 0! # max listen queue: 15 # If > 0, increase max_children # listen queue len: 4096 # idle processes: 8 # active processes: 42 # total processes: 50 # max active processes: 48 # max children reached: 12 # If > 0, increase max_children! ```

  1. 1.Find and fix slow requests:
  2. 2.```ini
  3. 3.# /etc/php/8.2/fpm/pool.d/www.conf

# Log requests slower than 5 seconds request_slowlog_timeout = 5s slowlog = /var/log/php8.2-slow.log

# Example slow log output: # [15-Jan-2024 10:30:00] [pool www] pid 12345 # script_filename = /var/www/html/api/orders.php # [0x00007f1234567890] file_get_contents() /var/www/html/lib/ApiClient.php:45 # [0x00007f1234567891] fetchOrderStatus() /var/www/html/api/orders.php:23

# This shows exactly which line is slow ```

  1. 1.Use pm = ondemand for variable traffic patterns:
  2. 2.```ini
  3. 3.# ondemand: spawn workers only when needed
  4. 4.pm = ondemand
  5. 5.pm.max_children = 200 # Absolute maximum
  6. 6.pm.process_idle_timeout = 30s # Kill idle workers after 30s

# Good for: environments with unpredictable traffic spikes # Bad for: high-traffic sites (spawn latency adds to response time) ```

Prevention

  • Monitor max children reached metric from FPM status page
  • Set up alerts when listen queue exceeds 0
  • Use request_slowlog_timeout to identify slow code paths
  • Configure Nginx fastcgi_read_timeout to match PHP max_execution_time
  • Load test to determine the right pm.max_children for your traffic
  • In Docker, set memory limits that account for max_children * memory_per_process
  • Consider switching to PHP-FPM + TCP (port 9000) instead of Unix sockets for high-traffic sites