PHP-FPM : pools, tuning et healthcheck

PHP-FPM : pools, tuning et healthcheck

Configurez PHP-FPM pour la production : pools dedies par site, gestion de la memoire, opcache, healthcheck, slow log. La cle d'un serveur PHP rapide et stable.

Introduction

PHP-FPM (FastCGI Process Manager) est le moyen standard d'executer PHP en prod derriere Nginx ou Apache. Il gere :

  • Un pool de workers PHP pre-forks
  • Le recyclage memoire (max_requests)
  • L'isolation par utilisateur / site
  • Le slow log pour identifier les requetes lentes
  • Le healthcheck pour le load balancer

Une mauvaise config = 502 Bad Gateway, OOM, RAM gaspillee. Une bonne config = stabilite et performance.

Prerequis

  • VPS Debian / Ubuntu
  • Nginx ou Apache deja installe
  • Connaissance de base PHP

Etape 1 : Installation

sudo apt update
sudo apt install -y php8.2-fpm php8.2-mysql php8.2-redis php8.2-curl php8.2-mbstring php8.2-xml php8.2-zip php8.2-gd php8.2-intl
sudo systemctl enable --now php8.2-fpm

Etape 2 : Anatomie d'un pool

/etc/php/8.2/fpm/pool.d/www.conf est le pool par defaut.

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

listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

pm.status_path = /fpm-status
ping.path = /fpm-ping

slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 5s

request_terminate_timeout = 60s

catch_workers_output = yes
php_admin_value[error_log] = /var/log/php-fpm/error.log
php_admin_flag[log_errors] = on

Etape 3 : Process Manager (pm)

3 modes :

  • static : nombre fixe de workers (pm.max_children). Predictible, RAM stable. Convient pour des charges constantes.
  • dynamic : varie entre min et max. Bon equilibre.
  • ondemand : workers crees a la demande, tues apres pm.process_idle_timeout. Tres econome en RAM, mais latence accrue.

Recommandation prod : dynamic pour la plupart des cas.

Etape 4 : Calcul du max_children

Formule : pm.max_children = (RAM disponible) / (RAM moyenne par worker)

Mesurer la RAM par worker :

ps -ylC php-fpm8.2 --sort:rss | head

Colonne RSS en Ko. Si moyenne 50 Mo, et 4 Go de RAM disponible pour PHP :

pm.max_children = 4096 / 50 = ~80

Restez conservateur : 60-70.

Etape 5 : Un pool par site

Pour isoler vos sites, creez un pool dedie par site :

sudo nano /etc/php/8.2/fpm/pool.d/site1.conf
[site1]
user = site1
group = site1
listen = /run/php/php8.2-site1.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 20
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5

slowlog = /var/log/php-fpm/site1-slow.log
request_slowlog_timeout = 3s

php_admin_value[open_basedir] = /var/www/site1:/tmp
php_admin_value[upload_tmp_dir] = /var/www/site1/tmp
php_admin_value[session.save_path] = /var/www/site1/sessions
php_admin_value[memory_limit] = 256M

Creez l'user :

sudo useradd -r -s /bin/false site1
sudo mkdir -p /var/www/site1/{tmp,sessions}
sudo chown -R site1:site1 /var/www/site1

Restart :

sudo systemctl restart php8.2-fpm

Etape 6 : Configuration Nginx pour le pool

server {
    listen 443 ssl http2;
    server_name site1.fr;
    root /var/www/site1;
    index index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-site1.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Etape 7 : Opcache

Opcache compile les scripts PHP en bytecode et le garde en RAM. C'est essentiel en prod.

/etc/php/8.2/fpm/php.ini :

opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.save_comments=1

En prod stable, mettez opcache.validate_timestamps=0 pour ne plus jamais re-verifier le disque (10-15% de gain perf, mais necessite un systemctl reload php8.2-fpm apres chaque deploiement).

Etape 8 : JIT (PHP 8+)

Le JIT compile certaines portions a la volee :

opcache.jit=1255
opcache.jit_buffer_size=128M

Le mode 1255 est equilibre. Gain : 5-15% sur du PHP intense (calculs), peu visible sur du Wordpress / Laravel classique.

Etape 9 : Slow log

Pour identifier les scripts lents :

slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 3s
sudo tail -f /var/log/php-fpm/slow.log

Format :

[16-May-2026 14:32:01] [pool www] pid 12345
script_filename = /var/www/site1/slow.php
[0x00007f8c12345000] sleep() /var/www/site1/slow.php:5
[0x00007f8c12345001] processData() /var/www/site1/slow.php:10

Identifie precisement la stack trace au moment du depassement.

Etape 10 : FPM status + monitoring

pm.status_path = /fpm-status
ping.path = /fpm-ping

Nginx :

location ~ ^/(fpm-status|fpm-ping)$ {
    access_log off;
    allow 127.0.0.1;
    deny all;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    include fastcgi_params;
}

Recuperer le status :

curl http://localhost/fpm-status?full

Affiche :

pool:                 www
process manager:      dynamic
total processes:      10
active processes:     2
idle processes:       8
listen queue:         0
max listen queue:     0
slow requests:        3

Integrez dans Prometheus avec php-fpm_exporter.

Etape 11 : Healthcheck pour load balancer

ping.path retourne pong si FPM est UP :

curl http://localhost/fpm-ping

Utilisez ce path dans votre load balancer (HAProxy, Traefik, AWS ELB).

Etape 12 : memory_limit et timeouts

Per pool :

php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 60
php_admin_value[max_input_time] = 60
php_admin_value[post_max_size] = 64M
php_admin_value[upload_max_filesize] = 64M

request_terminate_timeout (sigkill apres ce delai si pas de reponse) :

request_terminate_timeout = 65s

Mettez-le legerement plus grand que max_execution_time pour laisser PHP terminer proprement avant le kill.

Depannage

502 Bad Gateway

PHP-FPM down, surcharge, ou socket pas accessible :

sudo systemctl status php8.2-fpm
sudo tail -f /var/log/php-fpm/error.log
sudo journalctl -u php8.2-fpm -n 50

Verifiez les permissions du socket :

ls -la /run/php/php8.2-fpm.sock

"server reached pm.max_children setting"

Saturation. Augmentez pm.max_children, ou identifiez les requetes lentes via le slow log.

Memory leak

Reduisez pm.max_requests (recycle les workers plus souvent) :

pm.max_requests = 100

"MySQL has gone away"

Les workers FPM gardent des connexions ouvertes longtemps. Augmentez wait_timeout MySQL ou utilisez PDO::ATTR_PERSISTENT = false.

File upload limite

Verifiez upload_max_filesize, post_max_size, et la limite Nginx client_max_body_size.

Commandes utiles

# Status
sudo systemctl status php8.2-fpm

# Reload (no downtime)
sudo systemctl reload php8.2-fpm

# Tester la config
sudo php-fpm8.2 -t

# Lister les pools
sudo php-fpm8.2 -tt

# Monitorer en temps reel
watch 'curl -s http://localhost/fpm-status'

# RAM utilisee par pool
ps -eo rss,cmd | grep php-fpm | awk '{sum+=$1} END {print sum/1024 " MB"}'

# Top des process FPM
ps aux --sort=-rss | grep php-fpm | head

Conclusion

Une config PHP-FPM saine vous donne :

  • Memoire predictible
  • Isolation entre sites
  • Visibilite sur les requetes lentes
  • Healthcheck pour load balancer

Pour aller plus loin :

  • Combinez avec OPcache file cache (cache entre restarts)
  • Migrez vers RoadRunner pour des perf extremes (PHP daemon mode)
  • Utilisez FrankenPHP pour avoir PHP + serveur web integre

Ressources

Rejoignez notre serveur communautaire Discord

Pour toute question, suggestion ou simplement pour discuter avec la communauté, rejoignez-nous sur Discord !

900+Membres