Resumen: Esta guía técnica explica cómo fluye una solicitud en PHP-FPM, qué ajustes impactan la latencia y una regla práctica para dimensionar pm.max_children por memoria.
Incluye una configuración de ejemplo, comprobaciones operativas y pasos para validar cambios con pruebas de carga.
Introducción
PHP-FPM (FastCGI Process Manager) administra pools de procesos PHP. Cada worker atiende una solicitud a la vez; la concurrencia total de un pool es el número de children activos.
Si faltan workers el resto de solicitudes se encolan en el socket o generan 502/504 cuando el backlog se desborda. El dimensionado correcto y unas pocas opciones clave evitan timeouts y picos de latencia.
Prerrequisitos
Acceso de administrador al host para editar la configuración de PHP-FPM y Nginx, ejecutar comandos de medición (ps, curl) y ejecutar pruebas de carga desde clientes de prueba.
Permisos para leer/crear archivos de slowlog y exponer pm.status_path y ping.path de forma segura (por ejemplo, accesible sólo desde localhost o una red de gestión).
Desarrollo
En esta sección describo el procedimiento recomendado: exponer métricas, medir memoria por worker, calcular pm.max_children y ajustar tiempos y backlog.
Procedimiento
- Exponer estado y slow logs.
- Medir RSS por worker bajo carga realista.
- Calcular pm.max_children por presupuesto de memoria.
- Ajustar pm.start_servers y los spares para mantener workers calientes.
- Hacer pruebas de carga y iterar.
Detalles y comandos útiles para cada paso.
[app]
user = www-data
group = www-data
listen = /run/php/php-fpm-app.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
listen.backlog = 1024
pm = dynamic
pm.max_children = 32
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 16
pm.process_idle_timeout = 20s
pm.max_requests = 2000
request_terminate_timeout = 30s
request_slowlog_timeout = 2s
slowlog = /var/log/php-fpm/app.slow.log
pm.status_path = /status
ping.path = /ping
ping.response = pong
Lenguaje del código: TOML, también INI (ini)
Añade una ubicación en Nginx para exponer /status y /ping (restringe acceso):
location ~ ^/(status|ping)$ {
allow 127.0.0.1;
deny all;
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm-app.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Lenguaje del código: Nginx (nginx)
Comandos para comprobar estado y medir memoria por worker:
curl -s http://127.0.0.1/status?full
# listar RSS de procesos php-fpm (ajusta el comando según tu sistema)
ps -o pid,rss,cmd -C php-fpm | head -n 60
Lenguaje del código: Bash (bash)
Regla práctica para calcular pm.max_children:
max_children = floor(pool_memory_budget_mb / per_child_rss_mb). Empieza 10–20% por debajo del valor teórico para dejar cabeza.
import http from 'k6/http';
import { check } from 'k6';
export const options = {
scenarios: {
steady: {
executor: 'constant-arrival-rate',
rate: 200,
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 200,
maxVUs: 1000,
},
},
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<200'],
},
};
export default function () {
const res = http.get(`${__ENV.BASE_URL}/calc?x=1&y=2`);
check(res, {
'status is 200': r => r.status === 200,
'json has result': r => r.json('result') !== undefined,
});
}
Lenguaje del código: JavaScript (javascript)
Ejemplos
Mini caso: VM con 8 GB RAM, 2 GB reservados para sistema y servicios; 6 GB dedicados al pool. Muestras de RSS muestran mediana 120 MB por child.
Cálculo: floor(6000 / 120) = 50 → empezar con pm.max_children = 44 para dejar margen por fragmentación y OPcache.
Configuración de spares para absorber ráfagas en modo dynamic:
pm.start_servers = 12
pm.min_spare_servers = 12
pm.max_spare_servers = 24
pm.max_requests = 1500
Lenguaje del código: TOML, también INI (ini)
Validación: ejecutar carga sostenida y observar pm.status (idle, active, queue). Si la cola supera pm.max_children/4 durante >1 minuto, aumentar max_children si la memoria lo permite o optimizar endpoints lentos.
Checklist
- pm.status_path y ping.path habilitados y protegidos.
- request_slowlog_timeout = 1–2s para APIs; ruta de slowlog disponible.
- pm = dynamic y pm.max_children calculado por memoria.
- pm.max_requests en 1,000–3,000 según fuga observada.
- Alineación de request_terminate_timeout y timeouts de Nginx.
- OPcache con >20% de espacio libre en picos.
- listen.backlog ≥ 512 y límites de descriptors por encima del pico.
- Pools separados para admin/cron/colas si aplica.
Conclusión
PHP-FPM es medible: dimensiona por memoria, mantén workers calientes para ráfagas, activa slow logs y expón métricas. Valida con pruebas de carga y observa p95, cola y uso de memoria antes de escalar.
Si el patrón de trabajo no encaja (trabajos largos, streaming, altísima concurrencia ligera), considera arquitecturas alternativas en lugar de forzar más children.


