Back to site

PHP-FPM on Performance VPS

Tune the PHP-FPM pool (pm, pm.max_children, slowlog) on a HolyCloud Performance VPS to maximize PHP throughput without saturating RAM.

PHP-FPM on Performance VPS

PHP-FPM runs PHP scripts in processes separate from the web server. On a HolyCloud Performance VPS, a poorly sized pool causes 502 errors (exhausted workers) or saturated RAM (too many max_children).

Prerequisites

  • VPS with Nginx or Apache + PHP-FPM (e.g. PHP 8.2/8.3)
  • sudo access and knowledge of the pool path (/etc/php/8.x/fpm/pool.d/)
  • Per-worker memory estimate (see below)
  • Test or staging site to validate under load

Locate configuration

php-fpm8.3 -tt 2>/dev/null | head
ls /etc/php/8.3/fpm/pool.d/
sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Typical [www] pool:

[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500

Calculate pm.max_children

Starting formula:

max_children ≈ (RAM available for PHP) / (average memory per worker)

Measure worker memory under real load:

ps -o rss,cmd -C php-fpm8.3 | awk '{sum+=$1} END {print sum/NR/1024 " MB moyenne"}'

Example: 8 GB RAM VPS, OS + MySQL + Redis ≈ 3 GB left for PHP, worker ~80 MB:

5120 MB / 80 MB ≈ 64 → start at 40–50 and increase gradually

pm modes: which to choose?

| Mode | Usage |

|------|-------|

| dynamic | Default web — balance latency / RAM |

| ondemand | Very sporadic traffic — slower startup |

| static | Constant, predictable traffic — fixed RAM |

Frequent e-commerce production:

pm = dynamic
pm.max_children = 48
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 16

Timeouts and queues

request_terminate_timeout = 120s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm-slow.log

Avoid request_terminate_timeout = 0 (unlimited) — a stuck script monopolizes a worker.

Opcache (essential)

File /etc/php/8.3/mods-available/opcache.ini:

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0

validate_timestamps=0 in production after controlled deployment; set 1 in dev.

Restart:

sudo systemctl restart php8.3-fpm

Nginx: gateway to the socket

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_read_timeout 120s;
}

Verification under load

# FPM status (if pm.status_path enabled)
curl -s http://127.0.0.1/status?full

# logs
sudo tail -f /var/log/php8.3-fpm.log

Light HTTP test:

hey -n 1000 -c 50 https://votre-site.tld/

Monitor during the test:

watch -n1 'ps -C php-fpm8.3 | wc -l; free -h'

Advanced tuning

; avoid long leaks
pm.max_requests = 1000

; process manager — ping for healthcheck
pm.status_path = /fpm-status
ping.path = /fpm-ping

Protect /fpm-status by IP or Nginx auth.

Troubleshooting

| Symptom | Action |

|----------|--------|

| 502 Bad Gateway | Workers exhausted → increase max_children or optimize code |

| Swap increasing | Reduce max_children, enable Redis cache |

| PHP CPU 100% | Profile (Xdebug off in prod), object cache |

| Slowlog filling | SQL queries, loops, synchronous API calls |

Need help?

Attach to support: www.conf excerpt, free -h, slowlog excerpt (no sensitive client data), and Performance VPS plan.