PHP-FPM mal configurado es la causa más común de errores 502 y sitios lentos. Con la configuración correcta, tu WordPress o aplicación PHP puede manejar miles de visitas sin despeinarse.
Esta guía te enseña a optimizar PHP-FPM para máximo rendimiento.
¿Qué es PHP-FPM?
PHP-FPM (FastCGI Process Manager) es el gestor de procesos PHP recomendado para producción. Maneja las peticiones PHP de forma eficiente mediante pools de workers.
PHP-FPM vs mod_php
| Aspecto | PHP-FPM | mod_php (Apache) |
|---|---|---|
| Rendimiento | Mejor | Menor |
| Memoria | Eficiente | Más consumo |
| Compatibilidad | Nginx + Apache | Solo Apache |
| Aislamiento | Por pool | Compartido |
| Recomendación | ✅ Producción | Solo desarrollo |
Instalación
Ubuntu/Debian
# Instalar PHP-FPM (versión 8.2)
sudo apt update
sudo apt install php8.2-fpm php8.2-cli php8.2-common -y
# Extensiones comunes
sudo apt install php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring \
php8.2-xml php8.2-zip php8.2-intl php8.2-bcmath php8.2-redis -y
# Verificar
php -v
systemctl status php8.2-fpm
Múltiples versiones
# Instalar otra versión
sudo apt install php8.1-fpm -y
# Cambiar versión por defecto
sudo update-alternatives --set php /usr/bin/php8.2
Archivos de configuración
Ubicaciones importantes
# Configuración principal de PHP
/etc/php/8.2/fpm/php.ini
# Configuración de PHP-FPM
/etc/php/8.2/fpm/php-fpm.conf
# Pools (uno por archivo)
/etc/php/8.2/fpm/pool.d/www.conf
# Socket
/run/php/php8.2-fpm.sock
Configuración del Pool
Pool por defecto (www.conf)
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
[www]
; Usuario y grupo
user = www-data
group = www-data
; Socket (más rápido que TCP)
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Gestor de procesos
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500
; Logs
access.log = /var/log/php8.2-fpm.access.log
slowlog = /var/log/php8.2-fpm.slow.log
request_slowlog_timeout = 5s
; Status (para monitorización)
pm.status_path = /fpm-status
ping.path = /fpm-ping
Modos del Process Manager
| Modo | Comportamiento | Uso |
|---|---|---|
static | Workers fijos siempre | Tráfico constante alto |
dynamic | Workers según demanda | Mayoría de casos |
ondemand | Workers solo cuando hay peticiones | Poco tráfico |
Calcular workers óptimos
# Fórmula
max_children = (RAM disponible - RAM otros servicios) / RAM por proceso PHP
# RAM por proceso PHP (típico)
# WordPress simple: 30-50MB
# WordPress con plugins: 50-100MB
# Laravel/Symfony: 50-80MB
# Ejemplo VPS 2GB
# RAM disponible: 1.5GB (resto para sistema, MySQL, Nginx)
# RAM por proceso: 50MB
# max_children = 1500 / 50 = 30
Configuración por tamaño de VPS
VPS 1GB RAM:
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 4
VPS 2GB RAM:
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
VPS 4GB RAM:
pm = dynamic
pm.max_children = 40
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
VPS 8GB RAM:
pm = dynamic
pm.max_children = 80
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 40
Configuración de php.ini
Ajustes esenciales
sudo nano /etc/php/8.2/fpm/php.ini
; Memoria
memory_limit = 256M
; Uploads
upload_max_filesize = 64M
post_max_size = 64M
max_file_uploads = 20
; Tiempo de ejecución
max_execution_time = 300
max_input_time = 300
; Variables
max_input_vars = 5000
; Errores (producción)
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
; Timezone
date.timezone = Europe/Madrid
; Sesiones
session.save_handler = files
session.save_path = /var/lib/php/sessions
OPcache: Imprescindible
OPcache cachea el código PHP compilado, evitando recompilarlo en cada petición. Es la optimización más importante.
Habilitar OPcache
# Ya viene instalado, solo verificar
php -m | grep -i opcache
Configuración óptima
; En php.ini o /etc/php/8.2/fpm/conf.d/10-opcache.ini
[opcache]
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 60
opcache.validate_timestamps = 1
opcache.save_comments = 1
opcache.fast_shutdown = 1
; Para producción (no revalida archivos)
; opcache.validate_timestamps = 0
Verificar OPcache
<?php
// /var/www/opcache-status.php
print_r(opcache_get_status());
php /var/www/opcache-status.php | head -50
# O desde web (proteger con contraseña)
Pools separados por sitio
Por qué separar pools
- Aislamiento de recursos
- Diferentes configuraciones por sitio
- Un sitio problemático no afecta a otros
- Usuarios diferentes por seguridad
Crear pool adicional
sudo nano /etc/php/8.2/fpm/pool.d/sitio2.conf
[sitio2]
user = sitio2
group = sitio2
listen = /run/php/php8.2-fpm-sitio2.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
; Límites específicos
php_admin_value[memory_limit] = 128M
php_admin_value[upload_max_filesize] = 32M
php_admin_value[post_max_size] = 32M
; Logs separados
php_admin_value[error_log] = /var/log/php/sitio2-error.log
slowlog = /var/log/php/sitio2-slow.log
Nginx con pool específico
server {
server_name sitio2.com;
root /var/www/sitio2;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm-sitio2.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Integración con Nginx
Configuración básica
server {
listen 80;
server_name tudominio.com;
root /var/www/tudominio;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Timeouts
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
# Buffers
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
}
# Bloquear acceso a archivos sensibles
location ~ /\. {
deny all;
}
}
Habilitar status de PHP-FPM
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Ver status
curl http://localhost/fpm-status
curl http://localhost/fpm-status?full
Monitorización
Ver procesos PHP-FPM
# Procesos actuales
ps aux | grep php-fpm
# Contar workers
ps aux | grep "php-fpm: pool" | wc -l
# Memoria por proceso
ps -eo pid,comm,rss | grep php-fpm | awk '{sum+=$3} END {print "Total MB:", sum/1024}'
Status del pool
# Desde CLI
SCRIPT_NAME=/fpm-status \
SCRIPT_FILENAME=/fpm-status \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect /run/php/php8.2-fpm.sock
Script de monitorización
#!/bin/bash
# /root/scripts/php-fpm-status.sh
echo "=== PHP-FPM Status ==="
echo ""
echo "=== Procesos ==="
ps aux | grep "php-fpm: pool" | wc -l
echo "workers activos"
echo ""
echo "=== Memoria ==="
ps -eo pid,comm,rss | grep php-fpm | awk '{sum+=$3} END {print "Total:", sum/1024, "MB"}'
echo ""
echo "=== Pool Status ==="
curl -s "http://localhost/fpm-status" 2>/dev/null || echo "Status no disponible"
Errores comunes
Error 502 Bad Gateway
Causa: PHP-FPM no responde o socket no existe.
# Verificar que PHP-FPM está corriendo
systemctl status php8.2-fpm
# Verificar socket
ls -la /run/php/php8.2-fpm.sock
# Ver logs
tail -50 /var/log/php8.2-fpm.log
journalctl -u php8.2-fpm -n 50
Soluciones:
# Reiniciar PHP-FPM
sudo systemctl restart php8.2-fpm
# Verificar configuración
php-fpm8.2 -t
# Verificar permisos de socket
# listen.owner y listen.group deben coincidir con usuario de Nginx
Error 504 Gateway Timeout
Causa: Script PHP tarda demasiado.
; En php.ini
max_execution_time = 300
; En pool.d/www.conf
request_terminate_timeout = 300
# En Nginx
fastcgi_read_timeout 300;
”No pool defined"
# Verificar que existe al menos un pool
ls /etc/php/8.2/fpm/pool.d/
# Debe haber al menos www.conf
"Unable to start: Too many open files”
# Aumentar límites
sudo nano /etc/security/limits.conf
www-data soft nofile 65535
www-data hard nofile 65535
Workers siempre al máximo
# Ver si max_children es muy bajo
curl http://localhost/fpm-status
# Si "max children reached" > 0, aumentar max_children
# O hay memory leaks (revisar código/plugins)
Seguridad
Deshabilitar funciones peligrosas
; En php.ini
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
Ocultar versión PHP
expose_php = Off
Limitar acceso a directorios
; Restringir a directorio web
open_basedir = /var/www/:/tmp/:/var/lib/php/sessions/
Preguntas frecuentes
¿Cuántos workers de PHP-FPM necesito?
Depende de tu RAM. Calcula: (RAM disponible - RAM de otros servicios) / RAM por proceso PHP. Un proceso típico usa 30-100MB. Para VPS 2GB, 15-25 workers es razonable.
¿Por qué tengo errores 502 intermitentes?
Generalmente por max_children muy bajo. Cuando todas las conexiones están ocupadas, nuevas peticiones fallan. Aumenta max_children o optimiza el código para que los procesos terminen más rápido.
¿Debo usar socket o TCP para PHP-FPM?
Socket Unix es más rápido y seguro para conexiones locales. TCP solo si Nginx y PHP-FPM están en servidores diferentes.
¿OPcache es seguro en producción?
Sí, es imprescindible. Solo asegúrate de limpiar la caché al desplegar código nuevo. Con validate_timestamps=1, los cambios se detectan automáticamente (con ligero delay).
¿Cómo limpio la caché de OPcache?
Reiniciando PHP-FPM: systemctl restart php8.2-fpm. O programáticamente con opcache_reset() en un script PHP.
Nuestra recomendación
Configuración inicial:
- Calcular max_children según RAM
- Habilitar OPcache con 128MB
- Configurar logs de errores y slow queries
- Habilitar status para monitorización
Para alto rendimiento:
- Pools separados por sitio
- OPcache con validate_timestamps=0 (limpiar al deploy)
- Redis para sesiones
- Monitorización de workers
¿Necesitas PHP optimizado? El hosting WordPress de Avantys incluye PHP-FPM preconfigurado para máximo rendimiento.
Conclusión
PHP-FPM bien configurado es la diferencia entre un sitio que aguanta tráfico y uno que cae con 50 visitas. Ajusta max_children según tu RAM, habilita OPcache, y monitoriza regularmente.
Recuerda: más workers no siempre es mejor si no tienes RAM suficiente.
¿Necesitas un VPS optimizado para PHP? Explora los VPS de Avantys con LiteSpeed y configuración PHP optimizada.
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.