title: "Instalación y Configuración de PHP-FPM: Guía Completa para Rendimiento Óptimo" description: "Guía exhaustiva para instalar y configurar PHP-FPM (FastCGI Process Manager) con Nginx o Apache. Incluye optimización de rendimiento, gestión de pools, configuración de seguridad y mejores prácticas para entornos de producción." date: 2024-03-15 tags: ["PHP-FPM", "PHP", "Nginx", "Apache", "FastCGI", "Performance Optimization", "Web Server", "Process Management"] categories: ["Application Deployment", "Web Servers"]

Instalación y Configuración de PHP-FPM: Guía Completa para Rendimiento Óptimo

PHP-FPM (FastCGI Process Manager) es una implementación alternativa de PHP FastCGI con funcionalidades adicionales especialmente útiles para sitios web de alto tráfico. Esta guía cubre la instalación, configuración y optimización de PHP-FPM.

Tabla de Contenidos

¿Qué es PHP-FPM?

PHP-FPM (FastCGI Process Manager) es un gestor de procesos PHP alternativo que incluye:

  • Gestión avanzada de procesos: con stop/start suave
  • Pools de workers: capacidad para iniciar workers con diferentes uid/gid/chroot/environment
  • Logging adaptativo: para stdout y stderr
  • Reinicios de emergencia: en caso de destrucción accidental de caché opcode
  • Soporte de carga acelerada: con mecanismos especiales de cache opcode
  • Terminación lenta: con configuración de tiempo de espera ajustable
  • Página de estado: similar a mod_status de Apache

Ventajas sobre mod_php

  • Mejor rendimiento y uso de memoria
  • No requiere ejecutar el servidor web como usuario de PHP
  • Pools separados para diferentes sitios web
  • Estadísticas y estado incorporados
  • Mejor aislamiento y seguridad
  • Escalado más fácil

Requisitos Previos

Antes de comenzar, asegúrate de tener:

  • Un servidor con Ubuntu 20.04/22.04 o Debian 10/11
  • Acceso root o privilegios sudo
  • Nginx o Apache instalado
  • Conocimientos básicos de administración de servidores Linux

Instalación de PHP-FPM

En Ubuntu/Debian

# Update package repository
sudo apt update

# Install PHP-FPM and common extensions
sudo apt install php-fpm php-cli php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-zip php-opcache -y

# Check PHP-FPM version
php-fpm8.1 -v  # Replace 8.1 with your PHP version

# Check PHP-FPM status
sudo systemctl status php8.1-fpm

Instalar Versión Específica de PHP

Para instalar una versión específica de PHP:

# Add PHP repository
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update

# Install specific PHP version (example: PHP 8.2)
sudo apt install php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-opcache -y

En CentOS/RHEL

# Install EPEL and Remi repository
sudo yum install epel-release -y
sudo yum install https://rpms.remirepo.net/enterprise/remi-release-8.rpm -y

# Enable PHP 8.1 module
sudo dnf module enable php:remi-8.1 -y

# Install PHP-FPM
sudo yum install php-fpm php-cli php-mysqlnd php-curl php-gd php-mbstring php-xml php-zip php-opcache -y

# Start and enable PHP-FPM
sudo systemctl start php-fpm
sudo systemctl enable php-fpm

Configuración Básica

Estructura de Directorios de PHP-FPM

Ubicaciones importantes:

# Main configuration file
/etc/php/8.1/fpm/php-fpm.conf

# Pool configurations
/etc/php/8.1/fpm/pool.d/

# PHP configuration for FPM
/etc/php/8.1/fpm/php.ini

# Socket location
/var/run/php/php8.1-fpm.sock

# Log files
/var/log/php8.1-fpm.log

Archivo de Configuración Principal (php-fpm.conf)

# Edit main PHP-FPM configuration
sudo nano /etc/php/8.1/fpm/php-fpm.conf

Configuraciones importantes:

;;;;;;;;;;;;;;;;;;;;;
; Global Options    ;
;;;;;;;;;;;;;;;;;;;;;

; Pid file
pid = /run/php/php8.1-fpm.pid

; Error log file
error_log = /var/log/php8.1-fpm.log

; Log level
; Possible Values: alert, error, warning, notice, debug
log_level = notice

; Emergency restart threshold
emergency_restart_threshold = 10
emergency_restart_interval = 1m

; Process control timeout
process_control_timeout = 10s

; Maximum number of processes FPM can fork
process.max = 128

; Include pool configurations
include=/etc/php/8.1/fpm/pool.d/*.conf

Configuración de PHP (php.ini)

# Edit PHP configuration for FPM
sudo nano /etc/php/8.1/fpm/php.ini

Configuraciones recomendadas:

;;;;;;;;;;;;;;;;
; Performance  ;
;;;;;;;;;;;;;;;;

memory_limit = 256M
max_execution_time = 300
max_input_time = 300
upload_max_filesize = 64M
post_max_size = 64M
max_input_vars = 3000

;;;;;;;;;;;;;;;;
; Error Handling
;;;;;;;;;;;;;;;;

display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
error_log = /var/log/php_errors.log

;;;;;;;;;;;;;;;;
; Date/Time    ;
;;;;;;;;;;;;;;;;

date.timezone = America/New_York  ; Set your timezone

;;;;;;;;;;;;;;;;
; Security     ;
;;;;;;;;;;;;;;;;

expose_php = Off
allow_url_fopen = On
allow_url_include = Off

;;;;;;;;;;;;;;;;
; Session      ;
;;;;;;;;;;;;;;;;

session.save_handler = files
session.save_path = "/var/lib/php/sessions"
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1

;;;;;;;;;;;;;;;;
; OPcache      ;
;;;;;;;;;;;;;;;;

opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 2
opcache.fast_shutdown = 1

Gestión de Pools

Los pools permiten ejecutar diferentes sitios web con diferentes configuraciones y usuarios.

Configuración del Pool por Defecto

# Edit default pool configuration
sudo nano /etc/php/8.1/fpm/pool.d/www.conf

Configuración completa del pool:

;;;;;;;;;;;;;;;;;;;;;;;;;
; Pool www             ;
;;;;;;;;;;;;;;;;;;;;;;;;;

; Pool name
[www]

; Unix user/group of processes
user = www-data
group = www-data

; Socket or TCP/IP address
listen = /run/php/php8.1-fpm.sock

; Set permissions for unix socket
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Process manager settings
pm = dynamic

; Maximum number of child processes
pm.max_children = 50

; Number of child processes created on startup
pm.start_servers = 5

; Desired minimum number of idle server processes
pm.min_spare_servers = 5

; Desired maximum number of idle server processes
pm.max_spare_servers = 35

; Number of requests each child process should execute
pm.max_requests = 500

; URI to view FPM status page
pm.status_path = /status

; URI to view FPM ping page
ping.path = /ping

; Timeout for serving a single request
request_terminate_timeout = 300

; Slow log
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 5s

; Environment variables
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

; PHP configuration values
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/sessions

Crear un Pool Personalizado

Crea un pool separado para un sitio web específico:

# Create new pool configuration
sudo nano /etc/php/8.1/fpm/pool.d/example.conf

Configuración del pool:

; Pool for example.com
[example]

; Unix user/group
user = example
group = example

; Socket path
listen = /run/php/php8.1-fpm-example.sock

; Socket permissions
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Process manager
pm = dynamic
pm.max_children = 20
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 200

; Chroot environment
; chroot = /var/www/example.com

; Change directory
; chdir = /

; Status and ping
pm.status_path = /status
ping.path = /ping

; Slow log
slowlog = /var/log/php-fpm/example-slow.log
request_slowlog_timeout = 10s

; Timeout
request_terminate_timeout = 120

; Environment variables
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin

; PHP values
php_admin_value[error_log] = /var/log/php-fpm/example-error.log
php_admin_flag[log_errors] = on
php_value[memory_limit] = 128M

Crear usuario y directorio:

# Create user
sudo useradd -r -s /bin/false example

# Create necessary directories
sudo mkdir -p /var/www/example.com
sudo mkdir -p /var/log/php-fpm
sudo mkdir -p /var/lib/php/sessions

# Set ownership
sudo chown -R example:example /var/www/example.com

# Restart PHP-FPM
sudo systemctl restart php8.1-fpm

Gestionar Múltiples Pools

# List all pools
sudo ls -la /etc/php/8.1/fpm/pool.d/

# Test configuration
sudo php-fpm8.1 -t

# Restart specific pool (requires systemd service per pool)
sudo systemctl restart php8.1-fpm

# Check pool status via socket
sudo SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.1-fpm.sock

Integración con Nginx

Configuración Básica de Nginx

# Edit Nginx server block
sudo nano /etc/nginx/sites-available/example.com

Configuración completa:

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com/public_html;

    index index.php index.html index.htm;

    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP-FPM configuration
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;

        # Use Unix socket
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;

        # Or use TCP socket
        # fastcgi_pass 127.0.0.1:9000;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Timeouts
        fastcgi_connect_timeout 60;
        fastcgi_send_timeout 180;
        fastcgi_read_timeout 180;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
        fastcgi_intercept_errors on;
    }

    # Deny access to hidden files
    location ~ /\. {
        deny all;
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
}

Configuración con Pool Personalizado

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com/public_html;

    index index.php index.html;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;

        # Use custom pool socket
        fastcgi_pass unix:/run/php/php8.1-fpm-example.sock;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Habilitar Página de Estado de PHP-FPM

server {
    listen 127.0.0.1:80;
    server_name localhost;

    location ~ ^/(status|ping)$ {
        access_log off;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Probar y recargar Nginx:

# Test Nginx configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

Integración con Apache

Instalar mod_proxy_fcgi

# Enable required Apache modules
sudo a2enmod proxy_fcgi setenvif
sudo systemctl restart apache2

Configuración de VirtualHost

# Edit Apache VirtualHost
sudo nano /etc/apache2/sites-available/example.com.conf

Configuración:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public_html

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # PHP-FPM configuration
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
    </FilesMatch>

    # Or use TCP socket
    # <FilesMatch \.php$>
    #     SetHandler "proxy:fcgi://127.0.0.1:9000"
    # </FilesMatch>

    ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>

Habilitar sitio:

# Enable site
sudo a2ensite example.com.conf

# Test Apache configuration
sudo apache2ctl configtest

# Restart Apache
sudo systemctl restart apache2

Optimización del Rendimiento

Calcular Configuración Óptima del Pool

Calcula pm.max_children basado en RAM disponible:

# Check available RAM (in MB)
free -m

# Check average PHP-FPM process memory
ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/NR/1024 " MB"}'

# Formula: pm.max_children = (Total RAM - RAM for other services) / Average process size
# Example: (4096MB - 1024MB) / 50MB = 61

Configuración para Diferentes Escenarios

Servidor Pequeño (1-2GB RAM):

pm = dynamic
pm.max_children = 20
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 200

Servidor Mediano (4-8GB RAM):

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

Servidor Grande (16GB+ RAM):

pm = dynamic
pm.max_children = 100
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 20
pm.max_requests = 1000

Tráfico Alto y Predecible:

pm = static
pm.max_children = 50
pm.max_requests = 1000

Optimizar OPcache

# Edit PHP configuration
sudo nano /etc/php/8.1/fpm/conf.d/10-opcache.ini

Configuración optimizada:

; Enable OPcache
opcache.enable = 1
opcache.enable_cli = 0

; Memory settings
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000

; Validation
opcache.revalidate_freq = 2
opcache.validate_timestamps = 1

; Performance
opcache.fast_shutdown = 1
opcache.save_comments = 1

; File cache (optional)
; opcache.file_cache = /tmp/opcache

Usar TCP Socket vs Unix Socket

Unix Socket (Mejor para servidor único):

; In pool configuration
listen = /run/php/php8.1-fpm.sock
# In Nginx
fastcgi_pass unix:/run/php/php8.1-fpm.sock;

TCP Socket (Mejor para arquitectura distribuida):

; In pool configuration
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
# In Nginx
fastcgi_pass 127.0.0.1:9000;

Configuración de Seguridad

Limitar Extensiones de Archivo

; Only allow .php files to be executed
security.limit_extensions = .php

Deshabilitar Funciones Peligrosas

; In php.ini
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source

Configurar Chroot

; In pool configuration
chroot = /var/www/example.com
chdir = /

Configurar Variables de Entorno de Forma Segura

; Clear all environment variables
clear_env = yes

; Set only required variables
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

Restringir Acceso a Archivos

# Set correct ownership
sudo chown -R www-data:www-data /var/www/example.com

# Set directory permissions
sudo find /var/www/example.com -type d -exec chmod 755 {} \;

# Set file permissions
sudo find /var/www/example.com -type f -exec chmod 644 {} \;

# Protect sensitive files
sudo chmod 600 /var/www/example.com/.env

Monitoreo y Depuración

Habilitar Página de Estado

Accede a la página de estado:

# Via command line
sudo SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.1-fpm.sock

# Via browser (configure Nginx first)
# http://localhost/status
# http://localhost/status?full
# http://localhost/status?json

Salida de ejemplo:

pool:                 www
process manager:      dynamic
start time:           15/Mar/2024:10:30:45 +0000
start since:          3600
accepted conn:        12500
listen queue:         0
max listen queue:     5
listen queue len:     128
idle processes:       5
active processes:     10
total processes:      15
max active processes: 15
max children reached: 0
slow requests:        2

Analizar Logs Lentos

# View slow log
sudo tail -f /var/log/php-fpm/www-slow.log

# Find slowest scripts
sudo cat /var/log/php-fpm/www-slow.log | grep "script_filename" | sort | uniq -c | sort -rn | head -10

Monitorear con Herramientas del Sistema

# Monitor PHP-FPM processes
watch -n 1 'ps aux | grep php-fpm'

# Check process count
ps aux | grep php-fpm | wc -l

# Monitor memory usage
ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/1024 " MB"}'

# Use htop for interactive monitoring
sudo htop -p $(pgrep -d',' php-fpm)

Script de Monitoreo

Crea un script de monitoreo:

# Create monitoring script
sudo nano /usr/local/bin/php-fpm-monitor.sh

Contenido del script:

#!/bin/bash

# PHP-FPM Monitoring Script

echo "=== PHP-FPM Status ==="
date

# Process count
PROCESS_COUNT=$(ps aux | grep php-fpm | grep -v grep | wc -l)
echo "Active processes: $PROCESS_COUNT"

# Memory usage
MEMORY_USAGE=$(ps aux | grep php-fpm | grep -v grep | awk '{sum+=$6} END {print sum/1024}')
echo "Total memory usage: ${MEMORY_USAGE} MB"

# Average memory per process
AVG_MEMORY=$(echo "scale=2; $MEMORY_USAGE / $PROCESS_COUNT" | bc)
echo "Average memory per process: ${AVG_MEMORY} MB"

# CPU usage
CPU_USAGE=$(ps aux | grep php-fpm | grep -v grep | awk '{sum+=$3} END {print sum}')
echo "Total CPU usage: ${CPU_USAGE}%"

# Check status page
echo -e "\n=== Pool Status ==="
sudo SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.1-fpm.sock

# Recent errors
echo -e "\n=== Recent Errors ==="
sudo tail -n 10 /var/log/php8.1-fpm.log

Haz el script ejecutable:

sudo chmod +x /usr/local/bin/php-fpm-monitor.sh

# Run monitoring script
sudo /usr/local/bin/php-fpm-monitor.sh

Solución de Problemas

Errores Comunes y Soluciones

Error: "File not found" (404)

# Check socket permissions
ls -la /run/php/php8.1-fpm.sock

# Verify Nginx user can access socket
sudo -u www-data test -r /run/php/php8.1-fpm.sock && echo "OK" || echo "FAIL"

# Check fastcgi_param in Nginx
grep SCRIPT_FILENAME /etc/nginx/sites-available/example.com

Error: "502 Bad Gateway"

# Check if PHP-FPM is running
sudo systemctl status php8.1-fpm

# Check socket exists
ls -la /run/php/php8.1-fpm.sock

# Restart PHP-FPM
sudo systemctl restart php8.1-fpm

# Check error log
sudo tail -f /var/log/php8.1-fpm.log

Error: "504 Gateway Timeout"

Incrementa los valores de timeout:

; In pool configuration
request_terminate_timeout = 300
# In Nginx
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;

Error: Pool llega a max_children

# Check current pool status
sudo SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.1-fpm.sock | grep "max children reached"

# If value > 0, increase pm.max_children
sudo nano /etc/php/8.1/fpm/pool.d/www.conf
# Increase pm.max_children value

sudo systemctl restart php8.1-fpm

Comandos de Depuración

# Test PHP-FPM configuration
sudo php-fpm8.1 -t

# Run PHP-FPM in foreground for debugging
sudo php-fpm8.1 -F

# Check loaded configuration
sudo php-fpm8.1 -i | grep "Configuration File"

# List all pools
sudo php-fpm8.1 -i | grep "\[pool"

# Monitor error log in real-time
sudo tail -f /var/log/php8.1-fpm.log

# Check system resource usage
sudo systemctl status php8.1-fpm

# Verify PHP modules
php -m

# Check PHP-FPM socket connections
sudo ss -xl | grep php

Analizar Problemas de Rendimiento

# Install and use PHP-FPM status monitoring
sudo apt install php-fpm-fcgi -y

# Get detailed status
sudo SCRIPT_NAME=/status SCRIPT_FILENAME=/status QUERY_STRING=full REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.1-fpm.sock

# Identify slow queries
sudo grep "executing too slow" /var/log/php-fpm/www-slow.log

# Check for memory issues
sudo dmesg | grep -i php

# Monitor in real-time with top
sudo top -p $(pgrep -d',' php-fpm)

Conclusión

PHP-FPM es una poderosa herramienta para gestionar procesos PHP con mejor rendimiento, seguridad y flexibilidad que el tradicional mod_php. Con la configuración adecuada y el monitoreo regular, puede manejar aplicaciones web de alto tráfico de manera eficiente.

Puntos Clave:

  • PHP-FPM proporciona mejor rendimiento y escalabilidad que mod_php
  • Los pools permiten aislar diferentes sitios web con configuraciones únicas
  • La configuración adecuada de pm.max_children es crucial para el rendimiento
  • El monitoreo regular ayuda a identificar cuellos de botella
  • La configuración de seguridad previene muchas vulnerabilidades comunes
  • Los Unix sockets son generalmente más rápidos que los TCP sockets para configuraciones de servidor único

Mejores Prácticas:

  1. Siempre probar los cambios de configuración antes de aplicarlos en producción
  2. Monitorear el uso de recursos y ajustar la configuración del pool en consecuencia
  3. Usar pools separados para sitios web diferentes
  4. Habilitar OPcache para mejor rendimiento
  5. Implementar logging apropiado para depuración
  6. Realizar copias de seguridad de configuraciones antes de modificarlas
  7. Mantener PHP y PHP-FPM actualizados
  8. Revisar regularmente logs lentos para optimizar código

Próximos Pasos:

  1. Implementar múltiples versiones de PHP con PHP-FPM
  2. Configurar balanceo de carga con múltiples servidores PHP-FPM
  3. Integrar herramientas de monitoreo como New Relic o Datadog
  4. Implementar caché con Redis o Memcached
  5. Optimizar consultas de base de datos
  6. Configurar ajuste automático basado en métricas de rendimiento

Recuerda que la configuración óptima depende de tu carga de trabajo específica, recursos del servidor y requisitos de la aplicación. Monitorea regularmente y ajusta según sea necesario.