title: "Instalación del Stack LEMP (Linux, Nginx, MySQL, PHP): Guía Completa para Producción" description: "Aprende a instalar y configurar un stack LEMP completo (Linux, Nginx, MySQL, PHP) en Ubuntu/Debian. Incluye configuración de PHP-FPM, optimización de Nginx, seguridad de MySQL y mejores prácticas para entornos de producción." date: 2024-03-15 tags: ["LEMP", "Nginx", "MySQL", "PHP", "PHP-FPM", "Ubuntu", "Debian", "Web Server", "Stack Installation"] categories: ["Application Deployment", "Web Servers"]

Instalación del Stack LEMP (Linux, Nginx, MySQL, PHP): Guía Completa para Producción

El stack LEMP (Linux, Nginx, MySQL, PHP) es una alternativa popular al stack LAMP, que reemplaza Apache con Nginx. Esta guía cubre la instalación y configuración completa de un servidor LEMP listo para producción.

Tabla de Contenidos

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
  • Al menos 1GB de RAM (se recomiendan 2GB para producción)
  • Conexión a internet estable
  • Conocimientos básicos de la línea de comandos de Linux

Actualización del Sistema

Primero, actualiza la caché de repositorios de paquetes y actualiza todos los paquetes instalados:

# Update package repository cache
sudo apt update

# Update all installed packages
sudo apt upgrade -y

# Remove unnecessary packages
sudo apt autoremove -y

Instalación de Nginx

Instalar Nginx

# Install Nginx
sudo apt install nginx -y

# Start Nginx service
sudo systemctl start nginx

# Enable Nginx to start on boot
sudo systemctl enable nginx

# Check Nginx status
sudo systemctl status nginx

Verificar la Instalación de Nginx

Puedes verificar que Nginx esté ejecutándose visitando la IP de tu servidor en un navegador web:

# Get your server IP
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

Deberías ver la página de bienvenida predeterminada de Nginx.

Configurar el Firewall para Nginx

# Allow Nginx Full (HTTP and HTTPS)
sudo ufw allow 'Nginx Full'

# Check firewall status
sudo ufw status

Instalación de MySQL

Instalar el Servidor MySQL

# Install MySQL Server
sudo apt install mysql-server -y

# Start MySQL service
sudo systemctl start mysql

# Enable MySQL to start on boot
sudo systemctl enable mysql

# Check MySQL status
sudo systemctl status mysql

Asegurar la Instalación de MySQL

Ejecuta el script de instalación segura para mejorar la seguridad de MySQL:

# Run MySQL secure installation script
sudo mysql_secure_installation

Se te harán las siguientes preguntas:

  1. Validar plugin de contraseña: Elige Y si deseas aplicar políticas de contraseña
  2. Establecer contraseña root: Establece una contraseña segura para el usuario root de MySQL
  3. Eliminar usuarios anónimos: Elige Y para eliminar usuarios anónimos
  4. Deshabilitar inicio de sesión remoto de root: Elige Y para mayor seguridad
  5. Eliminar base de datos de prueba: Elige Y para eliminar la base de datos de prueba
  6. Recargar tablas de privilegios: Elige Y para aplicar cambios

Configurar la Autenticación de MySQL

Para Ubuntu 20.04 y versiones posteriores, el usuario root de MySQL está configurado para autenticarse usando el plugin auth_socket por defecto. Para usar autenticación por contraseña:

# Login to MySQL
sudo mysql

# Change authentication method for root user
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_strong_password';

# Flush privileges
FLUSH PRIVILEGES;

# Exit MySQL
EXIT;

Crear una Base de Datos y Usuario

# Login to MySQL
mysql -u root -p

# Create a database
CREATE DATABASE myapp_db;

# Create a user
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'secure_password';

# Grant privileges
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';

# Flush privileges
FLUSH PRIVILEGES;

# Exit MySQL
EXIT;

Instalación de PHP y PHP-FPM

Instalar PHP y Extensiones Comunes

A diferencia de Apache, Nginx no procesa PHP nativamente. Usaremos PHP-FPM (FastCGI Process Manager) para manejar archivos PHP.

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

# Check PHP version
php -v

# Check PHP-FPM status
sudo systemctl status php8.1-fpm  # Replace 8.1 with your PHP version

Para Ubuntu 22.04, la versión predeterminada de PHP es 8.1. Para Ubuntu 20.04, es PHP 7.4. Ajusta los comandos según tu versión.

Configurar PHP-FPM

El archivo de configuración principal está ubicado en:

  • Ubuntu 22.04 (PHP 8.1): /etc/php/8.1/fpm/php.ini
  • Ubuntu 20.04 (PHP 7.4): /etc/php/7.4/fpm/php.ini

Configuraciones recomendadas para producción:

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

Modifica las siguientes directivas:

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
max_input_time = 300
date.timezone = America/New_York  # Set your timezone

Configura el pool de PHP-FPM:

# Edit PHP-FPM pool configuration
sudo nano /etc/php/8.1/fpm/pool.d/www.conf

Configuraciones recomendadas:

; Process manager settings
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500

; Security settings
security.limit_extensions = .php

Reinicia PHP-FPM para aplicar los cambios:

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

# Check status
sudo systemctl status php8.1-fpm

Configuración de Nginx para PHP

Configurar el Bloque de Servidor Predeterminado

Edita el archivo de configuración predeterminado de Nginx:

# Edit default Nginx configuration
sudo nano /etc/nginx/sites-available/default

Reemplaza el contenido con la siguiente configuración:

server {
    listen 80;
    listen [::]:80;

    root /var/www/html;
    index index.php index.html index.htm;

    server_name your_domain.com www.your_domain.com;

    location / {
        try_files $uri $uri/ =404;
    }

    # PHP processing
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;  # Adjust PHP version
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Deny access to .htaccess files
    location ~ /\.ht {
        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;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
}

Probar y Recargar la Configuración de Nginx

# Test Nginx configuration
sudo nginx -t

# If test is successful, reload Nginx
sudo systemctl reload nginx

Prueba de la Instalación

Crear un Archivo de Información de PHP

# Create PHP info file
sudo nano /var/www/html/info.php

Agrega el siguiente contenido:

<?php
phpinfo();
?>

Guarda y cierra el archivo. Luego visita http://your_server_ip/info.php en tu navegador web. Deberías ver la página de información de PHP mostrando todos los detalles de configuración.

Importante: Elimina este archivo después de la verificación por razones de seguridad:

# Remove PHP info file
sudo rm /var/www/html/info.php

Crear una Página de Prueba PHP

# Create test PHP file
sudo nano /var/www/html/test.php

Agrega el siguiente contenido:

<?php
// Test MySQL connection
$servername = "localhost";
$username = "myapp_user";
$password = "secure_password";
$dbname = "myapp_db";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully to MySQL database!";

// Close connection
$conn->close();
?>

Visita http://your_server_ip/test.php para verificar la conectividad con la base de datos.

Configuración de Virtual Hosts

Crear un Nuevo Bloque de Servidor (Virtual Host)

Crea un nuevo archivo de configuración para tu sitio:

# Create new server block configuration
sudo nano /etc/nginx/sites-available/example.com

Agrega la siguiente configuración:

server {
    listen 80;
    listen [::]:80;

    root /var/www/example.com/public_html;
    index index.php index.html index.htm;

    server_name example.com www.example.com;

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

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        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;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
}

Crear el Directorio del Sitio Web

# Create website directory
sudo mkdir -p /var/www/example.com/public_html

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

# Set permissions
sudo chmod -R 755 /var/www/example.com

# Create test index file
echo "<h1>Welcome to example.com</h1>" > /var/www/example.com/public_html/index.html

Habilitar el Bloque de Servidor

# Create symbolic link
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# Test Nginx configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

Optimización del Rendimiento

Optimizar Nginx

Edita la configuración principal de Nginx:

# Edit Nginx main configuration
sudo nano /etc/nginx/nginx.conf

Configuraciones de rendimiento recomendadas:

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # Basic settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;

    # Buffer settings
    client_body_buffer_size 128k;
    client_max_body_size 64M;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 16k;

    # Timeout settings
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
    gzip_disable "msie6";

    # Include other configurations
    include /etc/nginx/mime.types;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Optimizar PHP-FPM

# Edit PHP-FPM pool configuration
sudo nano /etc/php/8.1/fpm/pool.d/www.conf

Ajusta según tus recursos del servidor:

; For a server with 2GB RAM
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

; Status page
pm.status_path = /status

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

Reinicia los servicios:

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

# Reload Nginx
sudo systemctl reload nginx

Optimizar MySQL

# Edit MySQL configuration
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Agrega las siguientes configuraciones bajo la sección [mysqld]:

# Connection settings
max_connections = 100
connect_timeout = 10
wait_timeout = 600

# Buffer settings
key_buffer_size = 16M
max_allowed_packet = 64M
thread_stack = 192K
thread_cache_size = 8

# Query cache (for MySQL 5.7 and earlier)
query_cache_limit = 1M
query_cache_size = 16M

# InnoDB settings
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

# Logging
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2

Reinicia MySQL:

# Restart MySQL
sudo systemctl restart mysql

Seguridad y Mejores Prácticas

Configurar SSL/TLS con Let's Encrypt

Instala Certbot para obtener certificados SSL gratuitos:

# Install Certbot
sudo apt install certbot python3-certbot-nginx -y

# Obtain SSL certificate
sudo certbot --nginx -d example.com -d www.example.com

# Test automatic renewal
sudo certbot renew --dry-run

Asegurar PHP

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

Configuraciones de seguridad recomendadas:

; Disable dangerous functions
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source

; Hide PHP version
expose_php = Off

; Enable error logging but disable display
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log

; Session security
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1

Configurar Fail2Ban

Instala Fail2Ban para proteger contra ataques de fuerza bruta:

# Install Fail2Ban
sudo apt install fail2ban -y

# Create Nginx jail configuration
sudo nano /etc/fail2ban/jail.local

Agrega la siguiente configuración:

[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log

[nginx-noscript]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 6

[nginx-badbots]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2

[nginx-noproxy]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2

Reinicia Fail2Ban:

# Restart Fail2Ban
sudo systemctl restart fail2ban

# Check status
sudo fail2ban-client status

Permisos de Archivos y Directorios

Establece permisos apropiados:

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

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

# Set ownership
sudo chown -R www-data:www-data /var/www

Copias de Seguridad Regulares

Crea un script de copia de seguridad:

# Create backup script
sudo nano /usr/local/bin/lemp-backup.sh

Agrega el siguiente contenido:

#!/bin/bash

# Backup configuration
BACKUP_DIR="/backups/lemp"
DATE=$(date +%Y%m%d_%H%M%S)
MYSQL_USER="root"
MYSQL_PASSWORD="your_mysql_root_password"

# Create backup directory
mkdir -p $BACKUP_DIR/$DATE

# Backup MySQL databases
mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD --all-databases > $BACKUP_DIR/$DATE/mysql_all_databases.sql

# Backup website files
tar -czf $BACKUP_DIR/$DATE/www.tar.gz /var/www

# Backup Nginx configuration
tar -czf $BACKUP_DIR/$DATE/nginx_config.tar.gz /etc/nginx

# Backup PHP configuration
tar -czf $BACKUP_DIR/$DATE/php_config.tar.gz /etc/php

# Remove backups older than 7 days
find $BACKUP_DIR -type d -mtime +7 -exec rm -rf {} \;

echo "Backup completed: $DATE"

Haz el script ejecutable y programa con cron:

# Make script executable
sudo chmod +x /usr/local/bin/lemp-backup.sh

# Add to crontab (daily at 2 AM)
sudo crontab -e

# Add this line:
0 2 * * * /usr/local/bin/lemp-backup.sh >> /var/log/lemp-backup.log 2>&1

Solución de Problemas

Verificar el Estado de los Servicios

# Check Nginx status
sudo systemctl status nginx

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

# Check MySQL status
sudo systemctl status mysql

# Check all service logs
sudo journalctl -xe

Errores Comunes de Nginx

Error: "502 Bad Gateway"

Esto generalmente significa que PHP-FPM no está ejecutándose o Nginx no puede conectarse a él.

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

# Check PHP-FPM socket
ls -la /var/run/php/

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

# Check Nginx error log
sudo tail -f /var/log/nginx/error.log

Error: "File not found" al ejecutar PHP

Verifica la configuración de fastcgi_param en tu bloque de servidor:

# Edit Nginx configuration
sudo nano /etc/nginx/sites-available/default

# Ensure this line exists in the PHP location block:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Problemas de Conexión con MySQL

# Test MySQL connection
mysql -u root -p

# Check MySQL error log
sudo tail -f /var/log/mysql/error.log

# Check MySQL port
sudo netstat -tlnp | grep mysql

Problemas de Permisos de PHP

# Check PHP-FPM user
grep "^user" /etc/php/8.1/fpm/pool.d/www.conf

# Ensure www-data owns the web files
sudo chown -R www-data:www-data /var/www

# Check file permissions
ls -la /var/www/html

Ver Logs

# Nginx access log
sudo tail -f /var/log/nginx/access.log

# Nginx error log
sudo tail -f /var/log/nginx/error.log

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

# MySQL error log
sudo tail -f /var/log/mysql/error.log

# System log
sudo tail -f /var/log/syslog

Conclusión

Ahora tienes un stack LEMP completamente funcional y optimizado corriendo en tu servidor. Esta configuración proporciona una base sólida para alojar aplicaciones web PHP con excelente rendimiento y seguridad.

Puntos Clave:

  • Nginx proporciona mejor rendimiento que Apache para contenido estático y alta concurrencia
  • PHP-FPM maneja el procesamiento de PHP de manera eficiente
  • MySQL almacena los datos de tu aplicación de forma segura
  • La configuración adecuada y las prácticas de seguridad son esenciales para producción
  • El monitoreo y las copias de seguridad regulares previenen pérdida de datos
  • Las actualizaciones continuas mantienen tu sistema seguro

Próximos Pasos:

  1. Configurar un certificado SSL con Let's Encrypt
  2. Implementar monitoreo del sistema (Nagios, Zabbix, o Prometheus)
  3. Configurar registro centralizado
  4. Implementar un sistema de caché (Redis o Memcached)
  5. Configurar un balanceador de carga para alta disponibilidad
  6. Implementar integración/despliegue continuo (CI/CD)

Recuerda mantener tu servidor actualizado y monitorear regularmente los registros para cualquier problema o actividad sospechosa.