Hosting Equipo Avantys 5 min

PHP-FPM en VPS: Configuración y Optimización

Optimiza PHP-FPM en tu VPS para máximo rendimiento. Configuración de pools, workers, OPcache, memoria y solución de errores 502/504.

// Compartir

PHP-FPM en VPS: Configuración y Optimización
PHP-FPM en VPS - configuración y optimización

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

AspectoPHP-FPMmod_php (Apache)
RendimientoMejorMenor
MemoriaEficienteMás consumo
CompatibilidadNginx + ApacheSolo Apache
AislamientoPor poolCompartido
Recomendación✅ ProducciónSolo 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

PHP-FPM pools y workers

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

ModoComportamientoUso
staticWorkers fijos siempreTráfico constante alto
dynamicWorkers según demandaMayoría de casos
ondemandWorkers solo cuando hay peticionesPoco 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

PHP OPcache configuración

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:

  1. Calcular max_children según RAM
  2. Habilitar OPcache con 128MB
  3. Configurar logs de errores y slow queries
  4. 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.

Hablar con Avantys
// Boletín

Suscríbete al boletín

Guías nuevas, sin spam. Cancela cuando quieras.