PHP-FPM ejecuta el código PHP en un conjunto de procesos (pool) que el servidor web, Nginx o Apache, llama a través de FastCGI. El tamaño de ese pool decide cuántas peticiones PHP se atienden a la vez: si es pequeño, las peticiones se quedan en cola y la web va lenta; si es demasiado grande, el servidor se queda sin memoria y empieza a usar swap o el kernel mata procesos. En este tutorial calcularás el tamaño correcto del pool de PHP-FPM 8.3 en Ubuntu 24.04 a partir de la memoria real que consumen tus procesos, elegirás el gestor de procesos adecuado y activarás las herramientas para comprobar que la configuración funciona bajo carga.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - PHP 8.3 con PHP-FPM instalado (
sudo apt install php8.3-fpm) y un servidor web que ya sirva tu aplicación PHP a través del socket/run/php/php8.3-fpm.sock. - La aplicación funcionando y recibiendo tráfico real o de prueba, para poder medir cuánta memoria usan los procesos.
Paso 1: Revisar la configuración actual del pool
Cada pool se define en un fichero dentro de /etc/php/8.3/fpm/pool.d/. Ubuntu instala uno llamado www en www.conf. Muestra sus directivas activas del gestor de procesos:
grep -E '^(user|group|listen|pm)' /etc/php/8.3/fpm/pool.d/www.conf
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
Con pm.max_children = 5 el pool atiende como máximo cinco peticiones PHP simultáneas. Es un valor pensado para máquinas mínimas y casi siempre se queda corto. Comprueba también si ya se ha alcanzado ese límite:
sudo grep 'max_children' /var/log/php8.3-fpm.log
[25-Sep-2026 10:41:12] WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
Si ves este aviso, las peticiones se han quedado esperando un proceso libre.
Paso 2: Medir la memoria de cada proceso PHP-FPM
El valor de pm.max_children sale de dividir la memoria que puedes dedicar a PHP entre lo que ocupa cada proceso. Con la aplicación recibiendo tráfico, calcula la memoria media (RSS) de los procesos php-fpm8.3:
ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1} END {printf "%d procesos, media %.0f MB\n", NR, s/NR/1024}'
6 procesos, media 58 MB
La cifra incluye al proceso maestro, que ocupa poco, así que la media real de los workers es algo mayor. Mide varias veces a lo largo del día y quédate con el valor más alto. Una web WordPress típica ronda los 40-80 MB por proceso; aplicaciones Laravel o Magento pueden superar los 100 MB.
Después, mira la memoria total y la que usan los demás servicios:
free -m
total used free shared buff/cache available
Mem: 3915 1210 512 45 2193 2402
Swap: 0 0 0
Paso 3: Calcular pm.max_children
La fórmula es:
pm.max_children = (RAM total - RAM para el resto de servicios) / RAM media por proceso
Ejemplo con un servidor de 4 GB en el que MySQL, Nginx, Redis y el sistema necesitan unos 1.500 MB:
(4000 - 1500) / 60 = 41 -> pm.max_children = 40
Redondea hacia abajo y deja margen: los procesos crecen con peticiones pesadas. Si el servidor solo ejecuta PHP-FPM y un Nginx, puedes reservar menos memoria para otros servicios, pero nunca asignes el 100 % de la RAM.
Paso 4: Elegir el gestor de procesos
La directiva pm define cómo se crean los procesos:
| Modo | Comportamiento | Cuándo usarlo |
|---|---|---|
dynamic | Mantiene un número de procesos en espera entre pm.min_spare_servers y pm.max_spare_servers, hasta pm.max_children | Opción por defecto, válida para la mayoría de servidores |
ondemand | No mantiene procesos en espera; los crea al llegar peticiones y los cierra tras pm.process_idle_timeout | Servidores con poca RAM o muchos pools con poco tráfico |
static | Mantiene siempre pm.max_children procesos | Servidores dedicados a una aplicación con tráfico alto y constante |
ondemand ahorra memoria pero añade la latencia de crear un proceso a la primera petición tras un periodo inactivo. static elimina esa latencia a cambio de ocupar la memoria de forma permanente.
Paso 5: Aplicar la nueva configuración
Abre el fichero del pool:
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Localiza las directivas pm y ajústalas. Este es un ejemplo con dynamic para el servidor de 4 GB del paso 3:
pm = dynamic
pm.max_children = 40
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500
Una regla práctica para dynamic es poner pm.start_servers en torno al 25 % de pm.max_children, pm.min_spare_servers al 10-15 % y pm.max_spare_servers al 35-40 %. pm.start_servers debe estar entre el mínimo y el máximo de procesos en espera, o PHP-FPM no arrancará.
pm.max_requests recicla cada proceso tras atender 500 peticiones, lo que contiene fugas de memoria de extensiones o de la propia aplicación. Con 0 los procesos no se reciclan nunca.
Si prefieres ondemand, la configuración equivalente es:
pm = ondemand
pm.max_children = 40
pm.process_idle_timeout = 10s
pm.max_requests = 500
Comprueba la sintaxis antes de aplicar nada:
sudo php-fpm8.3 -t
[25-Sep-2026 11:02:31] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
Recarga el servicio. reload reinicia los workers de forma ordenada sin cortar las peticiones en curso:
sudo systemctl reload php8.3-fpm
Paso 6: Limitar la duración de las peticiones y activar el slowlog
Una petición bloqueada (una consulta lenta, una API externa que no responde) ocupa un proceso indefinidamente. Añade estas líneas al final de www.conf para cortar las peticiones que superen 60 segundos y registrar la traza de las que tarden más de 5:
request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm-slow.log
request_terminate_timeout debe ser igual o mayor que el max_execution_time de PHP y que el timeout de tu servidor web (fastcgi_read_timeout en Nginx), o los errores serán confusos.
Comprueba la sintaxis y recarga:
sudo php-fpm8.3 -t && sudo systemctl reload php8.3-fpm
Cuando una petición supere los 5 segundos, verás en el slowlog la pila de llamadas que indica dónde estaba el script:
sudo tail -n 20 /var/log/php8.3-fpm-slow.log
[25-Sep-2026 11:20:04] [pool www] pid 31877
script_filename = /var/www/html/informe.php
[0x00007f3a1c2130a0] sleep() /var/www/html/informe.php:12
Paso 7: Activar la página de estado del pool
La página de estado muestra en tiempo real cuántos procesos están activos, si hay peticiones en cola y cuántas veces se ha alcanzado pm.max_children. Edita www.conf y descomenta la línea pm.status_path:
pm.status_path = /status
Recarga PHP-FPM:
sudo systemctl reload php8.3-fpm
Para consultarla sin exponerla en el servidor web, instala cgi-fcgi, que habla FastCGI directamente con el socket:
sudo apt install libfcgi-bin
sudo SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.3-fpm.sock
Content-type: text/plain;charset=UTF-8
pool: www
process manager: dynamic
start time: 25/Sep/2026:11:05:12 +0000
start since: 812
accepted conn: 15230
listen queue: 0
max listen queue: 0
listen queue len: 0
idle processes: 12
active processes: 3
total processes: 15
max active processes: 27
max children reached: 0
slow requests: 0
Los campos que importan:
listen queue: peticiones esperando un proceso libre ahora mismo. Debe ser 0 casi siempre.max active processes: el máximo de procesos ocupados a la vez desde el arranque. Si se acerca apm.max_children, estás cerca del límite.max children reached: cuántas veces se ha llegado al límite. Si crece y aún tienes memoria libre, subepm.max_children; si no tienes memoria, necesitas más RAM u optimizar la aplicación.slow requests: peticiones que han superadorequest_slowlog_timeout.
Paso 8: Separar aplicaciones en pools distintos
Si alojas varias aplicaciones, dales un pool propio a cada una. Así una web con un pico de tráfico no deja sin procesos a las demás y cada una se ejecuta con su propio usuario. Crea un pool nuevo para your_app:
sudo nano /etc/php/8.3/fpm/pool.d/your_app.conf
[your_app]
user = your_user
group = your_user
listen = /run/php/php8.3-fpm-your_app.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
pm.max_requests = 500
pm.status_path = /status
request_terminate_timeout = 60s
listen.owner y listen.group son www-data para que Nginx o Apache puedan escribir en el socket, mientras el código PHP se ejecuta como your_user. Ten en cuenta que la suma de pm.max_children de todos los pools es la que debe respetar el cálculo del paso 3.
Comprueba y recarga:
sudo php-fpm8.3 -t && sudo systemctl reload php8.3-fpm
ls -l /run/php/
srw-rw---- 1 www-data www-data 0 Sep 25 11:30 php8.3-fpm-your_app.sock
srw-rw---- 1 www-data www-data 0 Sep 25 11:30 php8.3-fpm.sock
Por último, apunta el virtual host de esa aplicación al nuevo socket. En Nginx:
fastcgi_pass unix:/run/php/php8.3-fpm-your_app.sock;
Solución de problemas
502 Bad Gatewaytras recargar: el socket no existe o el servidor web no tiene permisos. Compruebals -l /run/php/y que la ruta enfastcgi_passcoincide conlisten.- PHP-FPM no arranca: revisa
sudo journalctl -u php8.3-fpm -n 50. Condynamic, un error frecuente es quepm.start_serversquede fuera del rango entrepm.min_spare_serversypm.max_spare_servers. - El servidor usa swap o aparece el OOM killer:
pm.max_childrenes demasiado alto para la memoria disponible. Vuelve a medir la RAM por proceso y rebaja el valor. 504 Gateway Timeout: el servidor web se ha cansado de esperar antes que PHP. Revisa el slowlog para encontrar el código lento y alineafastcgi_read_timeoutconrequest_terminate_timeout.
Conclusión
Has dimensionado el pool de PHP-FPM a partir de la memoria real de tus procesos, has elegido el gestor de procesos adecuado y tienes la página de estado y el slowlog para confirmar que no hay peticiones en cola ni scripts bloqueados. Como siguientes pasos, activa y ajusta OPcache para reducir el tiempo de CPU de cada petición, añade una caché de datos con Memcached o Redis y mide el resultado con Apache Bench antes y después de cada cambio.
