Chi amministra server Linux con stack LEMP o LAMP sa bene che le prestazioni di PHP-FPM dipendono in larga misura dalla corretta configurazione del process manager (PM). L’impostazione predefinita pm = dynamic va bene per molti scenari, ma su server ad alto traffico con memoria disponibile può diventare un collo di bottiglia. Vediamo perché pm = static è spesso la scelta migliore per la produzione.
Le tre modalità di PHP-FPM process manager
PHP-FPM offre tre strategie per gestire i processi worker:
pm = dynamic
Il numero di processi varia dinamicamente in base ai parametri:
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
PHP-FPM mantiene un pool variabile: avvia un certo numero di processi al boot, ne crea di nuovi sotto carico e termina quelli in eccesso in fase di inattività. È la modalità più flessibile ma anche quella con più overhead gestionale.
pm = ondemand
pm = ondemand
pm.max_children = 50
pm.process_idle_timeout = 10s
I processi vengono creati solo quando arrivano le richieste e terminati dopo un timeout di inattività. Ideale quando la memoria è scarsa o si gestiscono molti pool con traffico basso (tipicamente hosting condiviso con cPanel). Il limite è che su server con traffico intermittente ma costante, i processi vengono continuamente creati e distrutti, aggiungendo latenza esattamente nei momenti di picco.
pm = static
pm = static
pm.max_children = 100
pm.max_requests = 1000
Il numero di processi è fisso: pm.max_children worker vengono avviati al boot e restano sempre in memoria, pronti a rispondere. Non c’è overhead di creazione/terminazione dei processi.
L’analogia con il CPU governor
La scelta tra le tre modalità rispecchia esattamente quella del governor CPUFreq su Linux:
- ondemand (CPU): scala la frequenza in base al carico, poi scende — stessa logica di
pm ondemand - conservative (CPU): simile ma più graduale — analogo a
pm dynamic - performance (CPU): massima frequenza sempre — equivalente a
pm static
Su un server di produzione dedicato con carico consistente, proprio come si imposta il governor a performance, ha senso impostare PHP-FPM a static: si sacrifica un po’ di memoria per azzerare la latenza di spawn dei processi.
Quando usare pm static
pm = static è la scelta giusta quando:
- Il server ha memoria abbondante rispetto al traffico atteso
- Il carico è costante o con picchi frequenti (non siti dormenti)
- Si gestisce un singolo pool PHP-FPM per applicazione
- Si vuole la latenza minima possibile per ogni richiesta
Con i worker già in memoria, un picco di traffico improvviso viene assorbito senza dover attendere lo spawn di nuovi processi — che su sistemi sotto carico può richiedere decine di millisecondi.
Calcolare il valore corretto di pm.max_children
Impostare pm.max_children a caso è il classico errore. Troppo alto esaurisce la RAM, troppo basso crea code di attesa. Ecco come calcolarlo con dati reali.
Step 1: misurare la dimensione media di un worker
ps --no-headers -o rss -C php-fpm | awk '{ sum += $1; n++ } END { print sum/n/1024 " MB" }'
Questo comando mostra la dimensione media in MB del Resident Set Size (RSS) di ogni processo php-fpm in esecuzione. Eseguirlo sotto carico reale, non a server scarico.
Step 2: applicare la formula
pm.max_children = memoria_allocabile_MB / dimensione_media_worker_MB
Esempio concreto: se il worker medio pesa 60 MB e si possono allocare 6 GB a PHP-FPM:
pm.max_children = 6144 / 60 ≈ 100
Importante: non assegnare tutta la RAM disponibile a PHP-FPM. Lasciare sempre headroom per il kernel, Nginx/Apache, il database (MySQL/PostgreSQL) e la cache del filesystem. Una regola empirica è non superare il 60-70% della RAM totale per PHP-FPM su un server LEMP monolitico.
Step 3: impostare pm.max_requests
pm.max_requests = 1000
Con pm = static, i processi non vengono mai riavviati automaticamente per inattività. pm.max_requests definisce dopo quante richieste un worker viene riavviato — utile per prevenire memory leak in applicazioni PHP non perfette. Un valore alto (1000+) riduce l’overhead mantenendo una certa protezione. Solo se si ha certezza assoluta di assenza di leak si può usare pm.max_requests = 0.
Monitoraggio dei processi PHP-FPM
Per verificare il comportamento in produzione:
# Vedere tutti i processi PHP-FPM con CPU e memoria
top -bn1 | grep php-fpm
# Contare i worker attivi vs idle (richiede pm.status_path abilitato)
curl http://localhost/fpm-status
# Dimensione totale RSS usata da PHP-FPM
ps --no-headers -o rss -C php-fpm | awk '{ sum += $1 } END { print sum/1024 " MB totali" }'
Abilitare il status page di PHP-FPM nel pool configuration è fondamentale per il monitoring:
; in /etc/php/8.x/fpm/pool.d/www.conf
pm.status_path = /fpm-status
Quando ondemand e dynamic restano la scelta giusta
Non tutto è nero o bianco. pm ondemand e pm dynamic restano preferibili in questi scenari:
- Hosting condiviso con 100+ pool: con tanti siti a traffico basso, tenere worker statici per ogni pool divorberebbe la RAM. cPanel stessa usa
ondemandcome default per questo motivo. - Server con memoria limitata: se la RAM è il collo di bottiglia, meglio sacrificare un po’ di latenza che andare in swap.
- Ambienti containerizzati con autoscaling orizzontale: in un setup Kubernetes dove i pod scalano orizzontalmente, ha più senso un
pm ondemandcon confini ben definiti per container, lasciando che l’orchestratore gestisca il scaling.
Esempio di configurazione ottimale per server ad alto traffico
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = static
pm.max_children = 80
pm.max_requests = 2000
pm.status_path = /fpm-status
request_terminate_timeout = 60s
request_slowlog_timeout = 10s
slowlog = /var/log/php/fpm-slow.log
php_admin_value[error_log] = /var/log/php/fpm-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
Conclusione
Su server dedicati ad alto traffico con memoria disponibile, pm = static è quasi sempre la configurazione vincente. Elimina l’overhead del process manager, garantisce latenza costante e rende il comportamento del sistema prevedibile sotto carico. La chiave è misurare prima di configurare: il valore di pm.max_children deve essere basato sulla dimensione reale dei worker in produzione, non su stime.
Per ambienti multi-pool o con memoria limitata, ondemand rimane una scelta sensata. Ma per il classico server LEMP di produzione con una singola applicazione, passare a pm static è spesso uno dei miglioramenti più semplici e impattanti che si possano fare.
Fonte originale: PHP-FPM tuning: Using ‘pm static’ for max performance — LinuxBlog.io