--- title: "Cómo desplegar Laravel en Ubuntu 24.04 con Nginx y MySQL" description: "Despliega Laravel en Ubuntu 24.04 con Nginx, PHP-FPM 8.3 y MySQL, con HTTPS de Let's Encrypt, la cola de trabajos en systemd y el programador de tareas en cron." category: "E-commerce y CMS" language: es source: https://cubepath.com/es/docs/e-commerce-y-cms/implementacion-de-aplicacion-laravel-en-linux updated: 2026-09-25 --- # Cómo desplegar Laravel en Ubuntu 24.04 con Nginx y MySQL Laravel es un framework PHP que en producción necesita un servidor web, PHP-FPM, una base de datos y un par de procesos auxiliares: el worker de colas y el programador de tareas. En este tutorial desplegarás una aplicación Laravel en Ubuntu 24.04 con Nginx, PHP 8.3, MySQL 8 y un certificado de Let's Encrypt, y dejarás la cola y el programador funcionando como servicios del sistema. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM. - Un usuario no root con privilegios `sudo`. En los ejemplos se llama `your_user`. - Un dominio con un registro DNS `A` apuntando a la IP del servidor. En los ejemplos se usa `your_domain`. - El código de tu aplicación en un repositorio Git, o las ganas de probar con un proyecto Laravel nuevo. Los ejemplos usan `myapp` como nombre de la aplicación y de la base de datos. Sustitúyelo por el tuyo en todos los comandos. ## Paso 1: Instalar Nginx, PHP 8.3 y MySQL Ubuntu 24.04 incluye PHP 8.3 en sus repositorios, que cumple los requisitos de las versiones actuales de Laravel. Instala Nginx, PHP-FPM con las extensiones que usa Laravel, MySQL, Composer y las utilidades para descomprimir paquetes: ```bash sudo apt update sudo apt install nginx mysql-server php8.3-fpm php8.3-cli php8.3-mysql php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-bcmath php8.3-intl composer git unzip ``` Comprueba las versiones instaladas: ```bash php -v composer --version ``` ```output PHP 8.3.6 (cli) (built: ...) (NTS) ... Composer version 2.7.1 ... ``` Verifica que PHP-FPM está activo, ya que Nginx le pasará las peticiones PHP a través de su socket: ```bash systemctl status php8.3-fpm --no-pager ``` ```output ● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager Active: active (running) since ... ``` Abre en el cortafuegos los puertos HTTP y HTTPS, además de SSH: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` ## Paso 2: Crear la base de datos En Ubuntu, el usuario `root` de MySQL se autentica por socket, así que puedes entrar con `sudo` sin contraseña: ```bash sudo mysql ``` Crea la base de datos y un usuario exclusivo para la aplicación. Sustituye `your_strong_password` por una contraseña larga y aleatoria: ```sql CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'your_strong_password'; GRANT ALL PRIVILEGES ON myapp.* TO 'myapp'@'localhost'; EXIT; ``` Comprueba que el nuevo usuario puede conectarse: ```bash mysql -u myapp -p -e "SHOW DATABASES;" ``` ```output +--------------------+ | Database | +--------------------+ | information_schema | | myapp | | performance_schema | +--------------------+ ``` ## Paso 3: Descargar el código de la aplicación Crea el directorio de la aplicación y haz a tu usuario propietario, con el grupo `www-data` que usan Nginx y PHP-FPM: ```bash sudo mkdir -p /var/www/myapp sudo chown your_user:www-data /var/www/myapp ``` Si tienes la aplicación en un repositorio, clónala e instala las dependencias sin los paquetes de desarrollo: ```bash git clone https://github.com/your_account/myapp.git /var/www/myapp cd /var/www/myapp composer install --no-dev --optimize-autoloader cp .env.example .env php artisan key:generate ``` Si solo quieres probar el despliegue, crea en su lugar un proyecto nuevo, que ya genera el archivo `.env` y la clave de la aplicación: ```bash composer create-project laravel/laravel /var/www/myapp cd /var/www/myapp ``` En ambos casos, comprueba que Artisan funciona y que la clave existe: ```bash php artisan --version grep APP_KEY .env ``` ```output Laravel Framework 12.x.x APP_KEY=base64:... ``` ## Paso 4: Configurar el entorno de producción Abre el archivo `.env`: ```bash nano /var/www/myapp/.env ``` Ajusta estas variables y deja el resto como están. Con `APP_DEBUG=false` los errores no se muestran al visitante, y los controladores `database` guardan sesiones, caché y cola en MySQL sin necesidad de instalar Redis: ```ini APP_ENV=production APP_DEBUG=false APP_URL=https://your_domain LOG_LEVEL=warning DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=myapp DB_USERNAME=myapp DB_PASSWORD=your_strong_password SESSION_DRIVER=database CACHE_STORE=database QUEUE_CONNECTION=database ``` > Nota: si tu aplicación es anterior a Laravel 11, la variable de la caché se llama `CACHE_DRIVER` en lugar de `CACHE_STORE`. ## Paso 5: Ajustar los permisos PHP-FPM se ejecuta como `www-data` y necesita escribir en `storage` (logs, sesiones, archivos subidos) y en `bootstrap/cache`. El resto del código solo necesita ser legible. Asigna el grupo `www-data`, da permiso de escritura al grupo solo en esos dos directorios y activa el bit `setgid` para que los archivos nuevos hereden el grupo: ```bash cd /var/www/myapp sudo chgrp -R www-data storage bootstrap/cache sudo chmod -R g+w storage bootstrap/cache sudo find storage bootstrap/cache -type d -exec chmod g+s {} + ``` El archivo `.env` contiene la contraseña de la base de datos. Haz que solo tu usuario y el grupo `www-data` puedan leerlo: ```bash chmod 640 .env sudo chgrp www-data .env ``` A partir de aquí, ejecuta los comandos de Artisan que escriben archivos como `www-data`. Así los logs y la caché que generan siempre pertenecen al usuario del servidor web y no aparecen errores de permisos más tarde. ## Paso 6: Ejecutar las migraciones y optimizar Crea las tablas de la aplicación. En producción, Laravel pide confirmación antes de migrar; `--force` la omite: ```bash sudo -u www-data php artisan migrate --force ``` ```output INFO Running migrations. 0001_01_01_000000_create_users_table ........................ 25.31ms DONE 0001_01_01_000001_create_cache_table ........................ 9.12ms DONE 0001_01_01_000002_create_jobs_table ......................... 21.40ms DONE ``` Crea el enlace simbólico `public/storage` para servir los archivos subidos y genera las cachés de configuración, rutas, eventos y vistas: ```bash sudo -u www-data php artisan storage:link sudo -u www-data php artisan optimize ``` > Importante: con la configuración en caché, Laravel ya no lee `.env` en cada petición. Cada vez que cambies `.env` vuelve a ejecutar `sudo -u www-data php artisan optimize`. ## Paso 7: Configurar Nginx Crea un bloque de servidor para el dominio: ```bash sudo nano /etc/nginx/sites-available/myapp ``` Pega esta configuración, basada en la recomendada por la documentación de Laravel. La raíz apunta a `public/`, de modo que `.env` y el resto del código nunca son accesibles desde la web: ```nginx server { listen 80; listen [::]:80; server_name your_domain www.your_domain; root /var/www/myapp/public; index index.php; charset utf-8; client_max_body_size 20M; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; 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 ~ ^/index\.php(/|$) { fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; } location ~ /\.(?!well-known).* { deny all; } } ``` Activa el sitio, desactiva el sitio por defecto y comprueba la sintaxis: ```bash sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Recarga Nginx y comprueba que la aplicación responde: ```bash sudo systemctl reload nginx curl -I http://your_domain ``` ```output HTTP/1.1 200 OK Server: nginx/1.24.0 (Ubuntu) Content-Type: text/html; charset=utf-8 ... ``` ## Paso 8: Activar HTTPS con Let's Encrypt Instala Certbot con su plugin para Nginx: ```bash sudo apt install certbot python3-certbot-nginx ``` Solicita el certificado. Certbot valida el dominio, añade la configuración TLS al bloque de servidor y crea la redirección de HTTP a HTTPS: ```bash sudo certbot --nginx -d your_domain -d www.your_domain ``` El paquete instala un temporizador de systemd que renueva los certificados automáticamente. Comprueba que la renovación funciona: ```bash sudo certbot renew --dry-run ``` Abre `https://your_domain` en el navegador: deberías ver tu aplicación, o la página de bienvenida de Laravel si creaste un proyecto nuevo. ## Paso 9: Ejecutar la cola de trabajos con systemd Los trabajos enviados a la cola (correos, notificaciones, procesos largos) solo se ejecutan si hay un worker escuchando. Crea un servicio de systemd para mantenerlo siempre en marcha: ```bash sudo nano /etc/systemd/system/myapp-queue.service ``` ```ini [Unit] Description=Laravel queue worker (myapp) After=network.target mysql.service [Service] User=www-data Group=www-data WorkingDirectory=/var/www/myapp ExecStart=/usr/bin/php /var/www/myapp/artisan queue:work --sleep=3 --tries=3 --max-time=3600 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` La opción `--max-time=3600` hace que el worker termine cada hora y systemd lo vuelva a lanzar, lo que evita que acumule memoria. Activa e inicia el servicio: ```bash sudo systemctl daemon-reload sudo systemctl enable --now myapp-queue systemctl status myapp-queue --no-pager ``` ```output ● myapp-queue.service - Laravel queue worker (myapp) Active: active (running) since ... ``` Puedes seguir lo que procesa con `journalctl -u myapp-queue -f`. ## Paso 10: Programar las tareas de Laravel Si la aplicación define tareas programadas, Laravel necesita que `schedule:run` se ejecute cada minuto. Edita el crontab de `www-data`: ```bash sudo crontab -u www-data -e ``` Añade esta línea al final: ```text * * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1 ``` Comprueba qué tareas tiene registradas la aplicación y cuándo se ejecutarán: ```bash sudo -u www-data php artisan schedule:list ``` ## Actualizar la aplicación Cuando publiques cambios, este es el orden habitual para desplegarlos. El modo mantenimiento muestra una página 503 mientras se aplican las migraciones: ```bash cd /var/www/myapp sudo -u www-data php artisan down git pull composer install --no-dev --optimize-autoloader sudo -u www-data php artisan migrate --force sudo -u www-data php artisan optimize sudo -u www-data php artisan queue:restart sudo -u www-data php artisan up ``` `queue:restart` indica al worker que termine tras el trabajo actual, y systemd lo arranca de nuevo con el código actualizado. ## Solución de problemas - **Error 500 sin detalle**: con `APP_DEBUG=false` el error solo aparece en `storage/logs/laravel.log`. Revísalo con `tail -n 50 /var/www/myapp/storage/logs/laravel.log`. - **`Permission denied` en `storage/logs/laravel.log`**: el archivo lo creó tu usuario al ejecutar Artisan sin `sudo -u www-data`. Corrige el propietario con `sudo chown -R www-data:www-data /var/www/myapp/storage/logs`. - **502 Bad Gateway**: Nginx no puede hablar con PHP-FPM. Comprueba que el servicio está activo con `systemctl status php8.3-fpm` y que la ruta del socket en Nginx es `/run/php/php8.3-fpm.sock`. - **Los cambios en `.env` no se aplican**: la configuración está en caché. Ejecuta `sudo -u www-data php artisan optimize` de nuevo. ## Conclusión Tu aplicación Laravel está en producción con Nginx, PHP-FPM 8.3 y MySQL, servida por HTTPS, con la cola de trabajos gestionada por systemd y el programador ejecutándose cada minuto. Como siguientes pasos puedes: - Instalar Redis y cambiar `CACHE_STORE`, `SESSION_DRIVER` y `QUEUE_CONNECTION` a `redis` si el tráfico crece. - Programar copias de seguridad diarias de la base de datos con `mysqldump`. - Automatizar la secuencia de actualización con una herramienta como Deployer o un pipeline de CI.