Módulo 5 · Lección 6 30 min

Despliegue en producción

Guía completa para desplegar una aplicación Laravel en producción: servidor VPS, Nginx, MySQL, variables de entorno, permisos y optimizaciones finales.

Introducción al despliegue

Desplegar una aplicación Laravel en producción implica configurar correctamente el servidor, la base de datos, las variables de entorno y aplicar una serie de optimizaciones para garantizar rendimiento y seguridad. En esta lección cubriremos el proceso completo usando un VPS (servidor privado virtual) con Ubuntu, Nginx y MySQL, que es la configuración más común.

Requisitos del servidor

Antes de empezar, necesitas un VPS con Ubuntu 22.04 o superior. Los requisitos mínimos son:

  • PHP 8.2+ con las extensiones necesarias
  • Nginx como servidor web
  • MySQL 8.0+ o MariaDB 10.6+
  • Composer
  • Node.js y npm (para compilar assets con Vite)
  • Git

Instalar las dependencias del servidor

Conéctate por SSH a tu servidor y ejecuta:

# Actualizar el sistema
sudo apt update && sudo apt upgrade -y

# Instalar PHP y extensiones necesarias para Laravel
sudo apt install -y php8.2 php8.2-fpm php8.2-mysql php8.2-mbstring \
  php8.2-xml php8.2-bcmath php8.2-curl php8.2-zip php8.2-intl \
  php8.2-redis php8.2-gd

# Instalar Nginx
sudo apt install -y nginx

# Instalar MySQL
sudo apt install -y mysql-server

# Instalar Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

# Instalar Node.js (usando nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install --lts

Configurar MySQL

sudo mysql_secure_installation

# Crear base de datos y usuario para la aplicación
sudo mysql -u root -p
CREATE DATABASE mi_app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'mi_app_user'@'localhost' IDENTIFIED BY 'contraseña_segura_aqui';
GRANT ALL PRIVILEGES ON mi_app_db.* TO 'mi_app_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Clonar y configurar la aplicación

# Ir al directorio web
cd /var/www

# Clonar el repositorio
sudo git clone https://github.com/tu-usuario/tu-repo.git mi-app

# Dar permisos al usuario actual
sudo chown -R $USER:www-data /var/www/mi-app
cd /var/www/mi-app

# Instalar dependencias de PHP (sin paquetes de desarrollo)
composer install --optimize-autoloader --no-dev

# Instalar dependencias de Node y compilar assets
npm install
npm run build

Configurar el archivo .env de producción

cp .env.example .env
nano .env

Configura el .env para producción:

APP_NAME="Mi Aplicación"
APP_ENV=production
APP_KEY=         # Se genera en el siguiente paso
APP_DEBUG=false  # MUY IMPORTANTE: false en producción
APP_URL=https://mi-dominio.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mi_app_db
DB_USERNAME=mi_app_user
DB_PASSWORD=contraseña_segura_aqui

CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=tu_usuario
MAIL_PASSWORD=tu_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@mi-dominio.com

Genera la clave de la aplicación y ejecuta las migraciones:

php artisan key:generate
php artisan migrate --force
php artisan db:seed --force  # Solo si tienes seeders necesarios

Permisos de directorios

# El servidor web necesita escribir en estas carpetas
sudo chown -R www-data:www-data /var/www/mi-app/storage
sudo chown -R www-data:www-data /var/www/mi-app/bootstrap/cache

sudo chmod -R 775 /var/www/mi-app/storage
sudo chmod -R 775 /var/www/mi-app/bootstrap/cache

# Enlace simbólico para el storage público
php artisan storage:link

Configurar Nginx

Crea el bloque de servidor para tu dominio:

sudo nano /etc/nginx/sites-available/mi-app
server {
    listen 80;
    listen [::]:80;
    server_name mi-dominio.com www.mi-dominio.com;

    root /var/www/mi-app/public;
    index index.php;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    charset utf-8;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Activa el sitio y reinicia Nginx:

sudo ln -s /etc/nginx/sites-available/mi-app /etc/nginx/sites-enabled/
sudo nginx -t  # Verificar que la configuración es válida
sudo systemctl restart nginx

Instalar SSL con Certbot

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d mi-dominio.com -d www.mi-dominio.com

Certbot configurará automáticamente HTTPS y la renovación automática del certificado.

Optimizaciones de producción

# Cachear configuración, rutas y vistas (ejecutar siempre en producción)
php artisan optimize

# O por separado:
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Si usas eventos
php artisan event:cache

Flujo de despliegue continuo

Cada vez que hagas una actualización, sigue este orden:

# 1. Activar modo mantenimiento (los usuarios ven una página de "Volvemos pronto")
php artisan down --retry=60

# 2. Descargar los últimos cambios
git pull origin main

# 3. Instalar nuevas dependencias
composer install --optimize-autoloader --no-dev

# 4. Compilar assets si han cambiado
npm install && npm run build

# 5. Ejecutar migraciones pendientes
php artisan migrate --force

# 6. Actualizar la caché
php artisan optimize

# 7. Reiniciar workers de cola
php artisan queue:restart

# 8. Desactivar modo mantenimiento
php artisan up

Variables críticas de seguridad

Antes de dar el sitio por listo, verifica estas configuraciones:

# APP_DEBUG debe ser false
grep APP_DEBUG .env

# APP_KEY debe estar generada (no vacía)
grep APP_KEY .env

# La carpeta .env NO debe ser accesible desde el navegador
# (Nginx la bloquea con location ~ /\.(?!well-known).*)

Con este proceso tendrás tu aplicación Laravel funcionando en producción de forma segura y optimizada. El mantenimiento continuo (actualizaciones de seguridad, monitoreo con Laravel Telescope u Horizon, backups de base de datos) es igualmente importante para mantener la aplicación saludable a largo plazo.

Felicidades por completar el módulo 5 y el curso completo. Ahora tienes las herramientas para construir aplicaciones Laravel profesionales, desde una API REST hasta el despliegue en producción.

Quiz

Pon a prueba lo aprendido

Responde las preguntas para comprobar que has entendido los conceptos clave.

1. ¿Qué comando de Artisan se debe ejecutar SIEMPRE en producción para optimizar el rendimiento de Laravel?

2. ¿Qué permisos deben tener los directorios storage/ y bootstrap/cache/ en el servidor?

3. ¿Cuál es la diferencia entre APP_ENV=production y APP_DEBUG=false en el archivo .env de producción?