laraveldeploynginxvpsproduccion

Cómo desplegar Laravel en un VPS con Nginx

Cómo desplegar Laravel en un VPS con Nginx

Desplegar una aplicación Laravel en un VPS puede parecer complejo la primera vez, pero una vez que entiendes los pasos y el rol de cada componente, el proceso es bastante directo. En esta guía vamos a desplegar Laravel en Ubuntu 22.04 con Nginx, PHP-FPM, MySQL, SSL gratis con Certbot y supervisord para los workers de cola.

Arquitectura del servidor

Antes de empezar, entendamos qué vamos a instalar y por qué cada componente es necesario:

  • Nginx: el servidor web que recibe las peticiones HTTP/HTTPS y las pasa a PHP-FPM.
  • PHP-FPM: el proceso de PHP que ejecuta tu aplicación Laravel.
  • MySQL: la base de datos.
  • Certbot: para obtener certificados SSL gratuitos de Let’s Encrypt.
  • Supervisord: para mantener el worker de colas siempre corriendo.
  • Git: para clonar y actualizar el código desde el repositorio.

Paso 1: Preparar el servidor

Conecta al VPS por SSH y actualiza el sistema:

ssh root@tu-ip-del-vps
apt update && apt upgrade -y

Crea un usuario no-root para mayor seguridad:

adduser deploy
usermod -aG sudo deploy
// Copia las claves SSH al nuevo usuario
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

A partir de ahora, trabaja con el usuario deploy:

su - deploy

Paso 2: Instalar Nginx

sudo apt install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx

Verifica que Nginx está corriendo:

sudo systemctl status nginx

Paso 3: Instalar PHP y sus extensiones

Agrega el repositorio de PHP:

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

Instala PHP 8.2 con las extensiones necesarias para Laravel:

sudo apt install -y \
    php8.2-fpm \
    php8.2-cli \
    php8.2-mysql \
    php8.2-mbstring \
    php8.2-xml \
    php8.2-curl \
    php8.2-zip \
    php8.2-bcmath \
    php8.2-tokenizer \
    php8.2-fileinfo \
    php8.2-gd \
    php8.2-redis

Verifica que PHP-FPM está corriendo:

sudo systemctl status php8.2-fpm

Paso 4: Instalar MySQL

sudo apt install -y mysql-server
sudo mysql_secure_installation

Crea la base de datos y el usuario para tu aplicación:

sudo mysql -u root -p

Dentro de MySQL:

CREATE DATABASE nombre_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password_muy_seguro_aqui';
GRANT ALL PRIVILEGES ON nombre_app.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Paso 5: Instalar Composer

curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer

Paso 6: Clonar el proyecto

// Crea el directorio para los proyectos web
sudo mkdir -p /var/www
sudo chown -R deploy:deploy /var/www

// Clona tu repositorio
cd /var/www
git clone https://github.com/tu-usuario/tu-proyecto.git mi-app
cd mi-app

Si el repositorio es privado, necesitarás configurar las claves SSH del servidor en tu cuenta de GitHub/GitLab.

Paso 7: Instalar dependencias y configurar el proyecto

// Instalar dependencias de PHP (sin las de desarrollo)
composer install --no-dev --optimize-autoloader

// Crear el archivo .env para producción
cp .env.example .env
nano .env

Configura el .env para producción:

APP_NAME="Mi Aplicación"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://tudominio.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=nombre_app
DB_USERNAME=app_user
DB_PASSWORD=password_muy_seguro_aqui

CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

REDIS_HOST=127.0.0.1
REDIS_PORT=6379

Genera la clave de aplicación:

php artisan key:generate

Paso 8: Configurar permisos

El servidor web (Nginx/PHP-FPM) corre como www-data. Las carpetas storage y bootstrap/cache deben ser escribibles por este usuario:

sudo chown -R deploy:www-data /var/www/mi-app
sudo chmod -R 755 /var/www/mi-app
sudo chmod -R 775 /var/www/mi-app/storage
sudo chmod -R 775 /var/www/mi-app/bootstrap/cache

Crea el enlace simbólico para el almacenamiento público:

php artisan storage:link

Paso 9: Ejecutar las migraciones

php artisan migrate --force

El flag --force es necesario en producción porque Laravel pide confirmación cuando el entorno es production.

Paso 10: Optimizar para producción

// Cachear la configuración
php artisan config:cache

// Cachear las rutas
php artisan route:cache

// Cachear las vistas Blade
php artisan view:cache

// Optimizar el autoloader de Composer
composer dump-autoload --optimize

O usa el comando que hace todo esto:

php artisan optimize

Paso 11: Compilar assets de frontend

Si tu proyecto tiene assets de frontend (Vite):

// Instalar Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

// Instalar dependencias y compilar
npm ci
npm run build

Paso 12: Configurar Nginx

Crea el archivo de configuración de Nginx para tu aplicación:

sudo nano /etc/nginx/sites-available/mi-app

Con este contenido:

server {
    listen 80;
    listen [::]:80;
    server_name tudominio.com www.tudominio.com;
    root /var/www/mi-app/public;

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

    index index.php;

    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 verifica la configuración:

sudo ln -s /etc/nginx/sites-available/mi-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Paso 13: Configurar SSL con Certbot

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d tudominio.com -d www.tudominio.com

Certbot detectará automáticamente tu configuración de Nginx, obtendrá el certificado de Let’s Encrypt y modificará el archivo de configuración para usar HTTPS. También configura la renovación automática.

Verifica que la renovación automática funciona:

sudo certbot renew --dry-run

Paso 14: Configurar el worker de colas con Supervisord

sudo apt install -y supervisor
sudo nano /etc/supervisor/conf.d/mi-app-worker.conf

Contenido del archivo:

[program:mi-app-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/mi-app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/mi-app/storage/logs/worker.log
stopwaitsecs=3600

Activa el worker:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start mi-app-worker:*

Paso 15: Configurar el scheduler de Laravel

Laravel tiene un programador de tareas (scheduler) que necesita una entrada en el crontab:

sudo crontab -e -u www-data

Agrega esta línea:

* * * * * cd /var/www/mi-app && php artisan schedule:run >> /dev/null 2>&1

Proceso de actualización del código

Cuando tienes que desplegar una nueva versión:

cd /var/www/mi-app

// Activar modo de mantenimiento (opcional)
php artisan down

// Obtener los últimos cambios
git pull origin main

// Actualizar dependencias PHP
composer install --no-dev --optimize-autoloader

// Actualizar dependencias de frontend y compilar
npm ci && npm run build

// Ejecutar nuevas migraciones
php artisan migrate --force

// Limpiar y regenerar la caché
php artisan optimize:clear
php artisan optimize

// Reiniciar los workers para que cojan el nuevo código
php artisan queue:restart

// Desactivar modo de mantenimiento
php artisan up

Instalar y configurar Redis

Si usas Redis para caché, sesiones o colas:

sudo apt install -y redis-server
sudo systemctl start redis
sudo systemctl enable redis

// Configurar Redis para mayor seguridad (desactivar comandos peligrosos)
sudo nano /etc/redis/redis.conf
// Cambia: bind 127.0.0.1 ::1  (para que solo escuche localmente)
// Agrega: requirepass tu_redis_password

sudo systemctl restart redis

Consejos de seguridad adicionales

Configurar el firewall

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

Deshabilitar el usuario root para SSH

sudo nano /etc/ssh/sshd_config
// Cambia: PermitRootLogin no
sudo systemctl restart sshd

Configurar fail2ban para proteger contra ataques de fuerza bruta

sudo apt install -y fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban

Conclusión

Desplegar Laravel en un VPS requiere seguir varios pasos, pero cada uno tiene su razón de ser. Nginx es el punto de entrada, PHP-FPM ejecuta tu aplicación, MySQL guarda los datos, Certbot asegura la conexión, y Supervisord mantiene los workers corriendo.

El punto más crítico es la configuración de permisos (Paso 8) y el archivo .env de producción con APP_DEBUG=false. Sin estos dos correctamente configurados, tu aplicación puede exponer información sensible o simplemente no funcionar.

Para proyectos serios, considera usar Laravel Forge que automatiza todo este proceso y añade funcionalidades adicionales como despliegues automáticos desde GitHub.