Saltar al contenido
Programming 2024-12-29 16 min de lectura

Cómo Implementar un Proyecto de Laravel en un VPS: Guía Paso a Paso

Desplegar una aplicación Laravel en un VPS es un proceso con varias piezas que deben encajar en orden: el sistema operativo asegurado, la base de datos, PHP con sus extensiones, el servidor web y, al final, la propia aplicación con su caché de producción y su certificado SSL. Hacerlo bien la primera vez ahorra horas de depuración de errores 500 sin mensaje. Esta guía recorre el proceso completo sobre Ubuntu 24.04, desde un VPS recién creado hasta la aplicación funcionando con HTTPS, e incluye los pasos de optimización que casi ninguna guía menciona y que marcan una diferencia de rendimiento medible.

Cómo Implementar un Proyecto de Laravel en un VPS: Guía Paso a Paso
#Programming#Laravel#PHP#VPS
T
Equipo Terranode
Editorial

Qué necesitas antes de empezar

  • Un VPS con Ubuntu 24.04 LTS (o Debian 13) y al menos 2 GB de RAM.
  • Acceso SSH como root o como usuario con sudo.
  • Un dominio con un registro A apuntando a la IP pública del servidor.
  • Tu proyecto Laravel en un repositorio Git accesible desde el servidor.

Sobre las versiones: Laravel 13 se publicó el 17 de marzo de 2026 y requiere PHP 8.3 o superior, admitiendo 8.3, 8.4 y 8.5. Ubuntu 24.04 trae PHP 8.3 en sus repositorios oficiales y Debian 13 trae PHP 8.4, así que ninguna de las dos necesita repositorios externos. Si tu aplicación aún usa Laravel 11 o 12, revisa su requisito de PHP antes de tocar el servidor.

Paso 1: Actualizar el sistema

Un servidor recién aprovisionado casi siempre tiene paquetes con parches pendientes, incluidos parches de seguridad. Actualizarlo antes de instalar nada evita arrastrar vulnerabilidades conocidas desde el primer día.

bash
sudo apt update && sudo apt upgrade -y

Paso 2: Crear un usuario sin privilegios de root

Operar de forma permanente como root significa que cualquier comando equivocado, o cualquier proceso comprometido, tiene control total del servidor. Un usuario normal con sudo obliga a un paso consciente antes de cada acción privilegiada.

bash
sudo adduser deploy
sudo usermod -aG sudo deploy

Cambia a ese usuario para el resto del proceso:

bash
su - deploy

Paso 3: Deshabilitar el acceso root por SSH

Bloquear el login directo de root elimina la mitad del trabajo de un atacante: ya no basta con adivinar una contraseña, hay que adivinar además un nombre de usuario. Es el ajuste de SSH con mejor relación entre esfuerzo y protección.

bash
sudo nano /etc/ssh/sshd_config

Localiza y ajusta:

text
PermitRootLogin no

Aplica el cambio:

bash
sudo systemctl restart ssh

Antes de cerrar tu sesión actual, abre una segunda terminal y comprueba que puedes entrar con el usuario deploy. Si algo salió mal, la sesión original sigue abierta para corregirlo. Una nota específica de Ubuntu 24.04: SSH usa activación por socket, así que si además cambias la directiva Port tendrás que ejecutar sudo systemctl daemon-reload && sudo systemctl restart ssh.socket para que el puerto nuevo tenga efecto.

Paso 4: Configurar el firewall

Permite SSH antes de activar el firewall, nunca al revés. UFW aplica por defecto la política de bloquear todo el tráfico entrante, así que activarlo sin la regla de SSH corta tu propia conexión de inmediato.

bash
sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
sudo ufw status

El perfil Apache Full abre los puertos 80 y 443 y solo existe una vez que Apache está instalado; si aún no lo has hecho, usa sudo ufw allow 80,443/tcp o vuelve a este paso después. En la guía de firewalls en Linux desarrollamos las políticas y cómo recuperarte si te bloqueas.

Paso 5: Instalar Apache

Apache sirve las peticiones HTTP y, con mod_php, ejecuta el código PHP dentro del mismo proceso. Es la opción más directa de configurar; más abajo comparamos cuándo conviene Nginx en su lugar.

bash
sudo apt install apache2 -y
sudo systemctl enable --now apache2

Visita http://tu_ip: si aparece la página por defecto de Apache, el tráfico externo llega bien al servidor.

Paso 6: Instalar y asegurar MySQL

El asistente mysql_secure_installation elimina las configuraciones inseguras por defecto: usuarios anónimos, base de datos de prueba y acceso root remoto. Saltárselo deja abiertas puertas que no aportan nada y sí figuran en cualquier escaneo automatizado.

bash
sudo apt install mysql-server -y
sudo mysql_secure_installation

Crea una base de datos y un usuario dedicados a la aplicación. Nunca uses el usuario root de MySQL en tu .env: si la aplicación se ve comprometida, un usuario limitado a una sola base de datos contiene el daño.

bash
sudo mysql
sql
CREATE DATABASE laravel_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'tu_contraseña_segura';
GRANT ALL PRIVILEGES ON laravel_db.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

El juego de caracteres utf8mb4 no es un detalle cosmético: es el que permite almacenar emojis y caracteres de cuatro bytes. Crear la base con el utf8 antiguo provoca errores al guardar contenido que los usuarios sí escriben todos los días.

Paso 7: Instalar PHP y sus extensiones

Laravel necesita un conjunto concreto de extensiones de PHP; si falta alguna, Composer se niega a instalar las dependencias con un mensaje que no siempre es claro. Instalarlas todas de una vez evita ese ciclo de prueba y error.

bash
sudo apt install php libapache2-mod-php php-mysql php-cli php-curl \
  php-mbstring php-xml php-zip php-bcmath php-gd php-intl -y
php -v

Qué hace cada una de las menos evidentes: mbstring maneja cadenas multibyte (acentos y ñ incluidos), bcmath hace aritmética de precisión arbitraria que usan varios paquetes de facturación, intl aporta formatos de fecha y número por idioma, y gd procesa imágenes.

Para producción conviene además ajustar dos valores de /etc/php/8.3/apache2/php.ini:

ini
memory_limit = 256M
upload_max_filesize = 20M
post_max_size = 20M

Paso 8: Instalar Composer

Composer es el gestor de dependencias de PHP: lee el archivo composer.json de tu proyecto y descarga las bibliotecas exactas que necesita. El paquete composer de los repositorios de Ubuntu funciona, pero suele ir por detrás de la versión oficial; instalarlo desde getcomposer.org garantiza la última estable.

bash
cd ~
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
rm composer-setup.php
composer --version

Paso 9: Descargar el proyecto

Clona el repositorio en /var/www/ y asigna la propiedad a tu usuario de despliegue, no a root. Si los archivos pertenecen a root, cada git pull posterior exigirá sudo y terminarás creando archivos con propietarios mezclados, que es el origen de la mitad de los problemas de permisos en Laravel.

bash
sudo mkdir -p /var/www
sudo chown deploy:deploy /var/www
cd /var/www
git clone https://github.com/tu_usuario/tu_proyecto.git
cd tu_proyecto
composer install --no-dev --optimize-autoloader

Las dos opciones de Composer son deliberadas: --no-dev omite herramientas de pruebas y depuración que no pintan nada en producción, y --optimize-autoloader genera un mapa de clases estático que evita que PHP busque archivos en disco en cada petición.

Paso 10: Configurar el archivo .env

El archivo .env guarda toda la configuración sensible de la aplicación y nunca debe estar en el repositorio Git. Se crea a partir de .env.example y se rellena directamente en el servidor.

bash
cp .env.example .env
nano .env
env
APP_NAME="Mi Aplicacion"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://tu_dominio.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=tu_contraseña_segura

APP_DEBUG=false es obligatorio en producción. Con true, cualquier error muestra al visitante la traza completa: rutas del servidor, fragmentos de código y, en algunos casos, variables de entorno con credenciales dentro. Es una filtración de información en toda regla.

Genera la clave de cifrado de la aplicación, que Laravel usa para las sesiones y los datos cifrados:

bash
php artisan key:generate

Paso 11: Ejecutar las migraciones

Las migraciones crean las tablas de la base de datos a partir del código del proyecto. En producción se ejecutan con --force, porque Laravel pide confirmación interactiva cuando detecta APP_ENV=production y en un script de despliegue no hay quién responda.

bash
php artisan migrate --force

Antes de ejecutarlas sobre una base con datos reales, haz una copia:

bash
mysqldump -u laravel_user -p laravel_db > ~/backup_$(date +%F).sql

Paso 12: Configurar el sitio virtual de Apache

El DocumentRoot debe apuntar a public/, nunca a la raíz del proyecto. Esta es la decisión de seguridad más importante de todo el despliegue: la raíz contiene .env con tus credenciales, y si es accesible por web cualquiera puede descargarlo.

bash
sudo nano /etc/apache2/sites-available/tu_proyecto.conf
apache
<VirtualHost *:80>
    ServerName tu_dominio.com
    ServerAlias www.tu_dominio.com
    DocumentRoot /var/www/tu_proyecto/public

    <Directory /var/www/tu_proyecto/public>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/tu_proyecto_error.log
    CustomLog ${APACHE_LOG_DIR}/tu_proyecto_access.log combined
</VirtualHost>

AllowOverride All permite que el .htaccess de Laravel funcione, y ese archivo es el que convierte cualquier URL en una petición a index.php. Sin él, la portada carga pero todas las demás rutas dan 404.

Activa el sitio, habilita la reescritura de URLs y desactiva el sitio por defecto:

bash
sudo a2ensite tu_proyecto.conf
sudo a2enmod rewrite
sudo a2dissite 000-default.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Paso 13: Ajustar los permisos correctamente

Laravel solo necesita escribir en dos directorios: storage/ y bootstrap/cache/. Dar la propiedad de todo el proyecto al usuario del servidor web es innecesario y significa que un fallo en la aplicación permitiría reescribir su propio código.

bash
sudo chown -R deploy:www-data /var/www/tu_proyecto
sudo find /var/www/tu_proyecto -type f -exec chmod 644 {} \;
sudo find /var/www/tu_proyecto -type d -exec chmod 755 {} \;
sudo chmod -R 775 /var/www/tu_proyecto/storage
sudo chmod -R 775 /var/www/tu_proyecto/bootstrap/cache
sudo chmod 640 /var/www/tu_proyecto/.env

El esquema resultante: el usuario deploy es dueño de los archivos y puede desplegar sin sudo; el grupo www-data (bajo el que corre Apache) puede leer todo y escribir solo donde Laravel lo necesita; y .env no es legible para el resto de usuarios del sistema.

Paso 14: Activar el certificado SSL

Certbot obtiene el certificado de Let's Encrypt, reescribe tu configuración de Apache para servir HTTPS y deja programada la renovación automática. No hace falta añadir ninguna tarea de cron a mano: el paquete instala su propio temporizador de systemd.

bash
sudo apt install certbot python3-certbot-apache -y
sudo certbot --apache -d tu_dominio.com -d www.tu_dominio.com

Comprueba que la renovación quedó activa:

bash
systemctl list-timers | grep certbot
sudo certbot renew --dry-run

Este punto gana importancia cada año: Let's Encrypt está acortando la vida de sus certificados. El perfil clásico sigue emitiendo a 90 días, pero el perfil tlsserver bajó a 45 días el 13 de mayo de 2026 y el clásico pasará a 64 días el 10 de febrero de 2027. Cualquier proceso de renovación manual dejará de ser sostenible.

Paso 15: Optimizar Laravel para producción

Estos cuatro comandos son lo que separa un despliegue funcional de uno rápido, y son el paso que más se omite. Laravel, sin ellos, lee y analiza en cada petición todos los archivos de configuración, todas las rutas y todas las vistas Blade.

bash
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

Qué hace cada uno: config:cache fusiona toda la configuración en un solo archivo PHP; route:cache compila la tabla de rutas; view:cache precompila las plantillas Blade; event:cache mapea los escuchadores de eventos.

Advertencia importante sobre config:cache: una vez ejecutado, Laravel deja de leer el archivo .env en tiempo de ejecución. Cualquier cambio posterior en .env no tendrá efecto hasta que ejecutes php artisan config:clear y vuelvas a cachear. Es la causa número uno de «cambié la contraseña de la base de datos y sigue fallando».

Si tu aplicación usa colas, añade también el trabajador en segundo plano con un gestor de procesos como Supervisor, para que sobreviva a reinicios:

bash
sudo apt install supervisor -y

Apache o Nginx: cuál elegir para Laravel

Apache con mod_php es más simple de configurar; Nginx con PHP-FPM aprovecha mejor la memoria en servidores pequeños. Ambos sirven Laravel perfectamente y la elección rara vez es el cuello de botella real de una aplicación.

CriterioApache + mod_phpNginx + PHP-FPM
Configuración inicialMás simple, .htaccess funciona directoRequiere bloque location con try_files
Consumo de RAM por peticiónMayor: PHP vive dentro de cada procesoMenor: PHP corre en un pool aparte
Archivos estáticosCorrectoMás eficiente
Configuración por directorioSí, con .htaccessNo, todo en el archivo del sitio
Muchas conexiones simultáneasAceptableMejor, modelo asíncrono
Cuándo convieneMigras desde hosting compartido, usas .htaccessVPS con poca RAM, mucho tráfico o muchos estáticos

Si tu VPS tiene 1 o 2 GB de RAM y esperas tráfico real, Nginx con PHP-FPM te dará más margen. Si vienes de un hosting compartido y tu proyecto depende de reglas en .htaccess, Apache te ahorra una traducción.

Errores frecuentes tras el despliegue

SíntomaCausa habitualSolución
Error 500 sin mensajePermisos de storage/ o clave sin generarchmod -R 775 storage y php artisan key:generate
La portada carga, el resto da 404Falta a2enmod rewrite o AllowOverride AllHabilitar módulo y opción, recargar Apache
El navegador muestra el código PHPFalta libapache2-mod-php o no está habilitadoInstalar el módulo y recargar Apache
Cambié .env y no surte efectoLa configuración está cacheadaphp artisan config:clear y volver a cachear
SQLSTATE[HY000] [1045]Credenciales incorrectas en .envProbar con mysql -u laravel_user -p
Se ve el listado de archivos del proyectoDocumentRoot apunta a la raíz, no a public/Corregirlo de inmediato: tu .env está expuesto
Fallos aleatorios de escrituraArchivos con propietarios mezclados por usar sudoReaplicar chown -R deploy:www-data

Los logs que resuelven casi todo:

bash
sudo tail -f /var/log/apache2/tu_proyecto_error.log
tail -f /var/www/tu_proyecto/storage/logs/laravel.log

Cómo actualizar la aplicación sin romper nada

Un despliegue posterior no repite la instalación: actualiza el código, las dependencias y las cachés, en ese orden y con el sitio en modo mantenimiento. Guarda esta secuencia como script y úsala siempre igual.

bash
cd /var/www/tu_proyecto
php artisan down
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan up

php artisan down muestra a los visitantes una página de mantenimiento en lugar de errores a medio camino mientras las migraciones se aplican. php artisan up la retira. Si algo falla entre medias y el sitio queda en mantenimiento, php artisan up lo devuelve a la normalidad.

Poner una aplicación Laravel en producción implica coordinar sistema operativo, base de datos, PHP, servidor web y SSL, pero en el orden correcto el proceso es predecible y repetible. La checklist para futuros despliegues: actualizar y asegurar el VPS, crear usuario sin root y firewall con SSH permitido primero, instalar el stack con PHP 8.3 o superior, clonar el proyecto con composer install --no-dev --optimize-autoloader, configurar .env con APP_DEBUG=false, apuntar el DocumentRoot a public/, ajustar permisos solo donde hacen falta, activar HTTPS y cachear configuración, rutas y vistas. Los dos errores que más caro salen son dejar el DocumentRoot en la raíz del proyecto, que expone tu .env, y olvidar que config:cache congela la configuración hasta que la limpies. Si todavía estás eligiendo dónde alojar el proyecto, en hosting compartido vs VPS vs dedicado explicamos por qué Laravel necesita acceso a la línea de comandos y, por tanto, un VPS. Si tienes dudas durante la implementación, nuestro equipo de soporte técnico está disponible 24/7 para ayudarte.

¿Listo para llevar tu sitio al siguiente nivel?

Hosting con LiteSpeed, NVMe y soporte 24/7 desde $3/mes.

Ver planes de hosting

Preguntas frecuentes