Optimal PHP-FPM pm.max_children Configuration for Multi-Site Hosting Environments


2 views

When configuring PHP-FPM for multi-site hosting, the memory calculation becomes significantly more complex than single-server scenarios. The critical parameter pm.max_children must account for:

  • Shared server resources across all sites
  • Peak concurrent usage patterns
  • Process memory variability (40-80MB in your case)

The basic formula Total RAM / Max Process Size becomes dangerous in multi-tenant environments:

# Problematic calculation example
total_ram = 5120  # 5GB in MB
max_process = 80
naive_max_children = total_ram / max_process  # Returns 64

This approach fails because it assumes only one application exists on the server. When multiplied by 30 sites, you correctly identified the potential memory explosion (76,800MB).

For your 30-site scenario with 5GB allocation, consider these approaches:

Method 1: Equal Share Partitioning

max_children_per_site = (total_ram * safety_factor) / (avg_process_size * site_count)
# Example with 0.8 safety factor:
(5120 * 0.8) / (40 * 30) = 3.41 → Round down to 3

Method 2: Tiered Allocation

Create priority groups in your PHP-FPM pool configurations:

; High-traffic site
[highpriority]
pm.max_children = 8

; Medium-traffic site 
[mediumpriority]
pm.max_children = 4

; Low-traffic site
[lowpriority]  
pm.max_children = 2

Implement real-time adjustment using these metrics:

  • Track actual memory usage per pool: ps -ylC php-fpm --sort:rss
  • Monitor active processes: pm.status_path = /status
  • Set emergency limits: emergency_children = max_children + 10%

Here's a safe baseline configuration for your environment:

[www]
user = www-data
group = www-data

listen = /run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 3
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 2
pm.process_idle_timeout = 10s
pm.max_requests = 500

; Emergency overflow
php_admin_value[memory_limit] = 96M
php_admin_flag[emergency_restart_threshold] = 10
php_admin_flag[emergency_restart_interval] = 1m

Always validate with realistic traffic patterns:

# Sample load test command
siege -c 50 -t 2M -b http://yoursite.com/test.php

# Monitor memory during test
watch -n 1 "free -m && ps -eo pid,user,%mem,command | grep php-fpm"

When managing multiple PHP-FPM pools on a shared server, the traditional calculation method for pm.max_children becomes insufficient. The standard formula:

pm.max_children = Available RAM / Largest Process Size

works well for single-application deployments but fails to account for resource contention in multi-tenant environments. Here's why this breaks down:

  • Each FPM pool can independently spawn processes up to its max_children limit
  • Process memory usage varies between applications (40-80MB in your case)
  • Simultaneous traffic spikes across sites create multiplicative memory demands

For your specific 8GB server hosting 30 sites with these characteristics:

Total available RAM: 5120MB (5GB dedicated to PHP-FPM)
Average process size: 40MB
Worst-case process size: 80MB
Number of websites: 30

The proper calculation requires two steps:

Step 1: Absolute System Limit

Absolute max = Total RAM / Largest Process
5120MB / 80MB = 64 processes (system-wide)

Step 2: Per-Pool Allocation

Per-pool max = Absolute max / Number of pools
64 / 30 ≈ 2 processes per pool

Instead of static allocations, consider implementing these advanced techniques:

; Dynamic scaling in php-fpm.conf
pm = dynamic
pm.max_children = 4  ; Absolute ceiling per pool
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 2
pm.max_requests = 500

For a server running Nginx with multiple pools:

; /etc/php/8.2/fpm/pool.d/site1.conf
[site1]
user = site1
group = site1
listen = /run/php/site1.sock

pm = dynamic
pm.max_children = 4
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 2
pm.max_requests = 500

php_admin_value[memory_limit] = 96M

Implement these monitoring solutions:

# Live process monitoring
sudo watch -n 2 'ps -ylC php-fpm --sort:rss | awk \'{print $8,$10}\''

# Memory tracking script
#!/bin/bash
TOTAL=$(free -m | awk \'/Mem:/ {print $2}\')
USED=$(ps -C php-fpm -o rss= | awk \'{sum+=$1} END {print sum/1024}\')
echo "PHP-FPM using ${USED}MB of ${TOTAL}MB total RAM"
  • Implement OPCache to reduce process memory requirements
  • Consider process recycling with pm.max_requests to prevent memory leaks
  • For high-traffic sites, isolate them to separate pools with higher limits
  • Monitor actual peak usage patterns before finalizing limits