Cargando…

Esto está tardando más de lo esperado.

Volver al centro de ayuda

Despliega tu proyecto

Qué necesita el servidor, qué corre una versión, y los tres ajustes que la gente olvida.

Esta guía es el procedimiento de publicación: qué necesita el servidor antes del primer despliegue, los comandos que corre cada despliegue, los valores de producción del entorno, y los procesos que tienen que seguir corriendo después. La necesitas una vez para el primer servidor y otra cada vez que una versión se comporta distinto que en tu máquina.

Lo que necesita el servidor

  • PHP 8.2 o más nuevo con las extensiones que Laravel exige (ctype, curl, dom, fileinfo, filter, hash, mbstring, openssl, pcre, pdo, session, tokenizer, xml), el driver de tu base de datos (pdo_mysql, pdo_pgsql o pdo_sqlite), simplexml y json para el SDK de AWS, y gd para las miniaturas de imágenes y pwa:generate-assets. bcmath o gmp es opcional y acelera las firmas de web push.
  • Composer, y Node con npm para compilar los assets. La compilación usa Vite 7, que pide un Node LTS actual (20.19 o más nuevo).
  • MySQL, PostgreSQL o SQLite. La cola, el caché y las sesiones usan por omisión tablas de la base de datos, así que una sola base de datos basta para empezar.
  • Un servidor web cuya raíz de documentos sea public/, detrás de HTTPS con un certificado real. Web push, la PWA instalable y la cookie de sesión SameSite=None que la PWA activa se niegan a funcionar sobre HTTP plano. bootstrap/app.php confía en todos los proxies (trustProxies(at: '*')), así que un balanceador que termina TLS se detecta correctamente.
  • Cron, para correr el programador una vez por minuto, y Supervisor o un equivalente para mantener vivo el worker de la cola. Ambos se configuran más abajo.
  • Almacenamiento compatible con S3 cuando FILESYSTEM_DISK se queda en su valor por omisión: el disco por omisión en config/filesystems.php es s3, no local.

La publicación

Corre esto en cada despliegue, desde la raíz del proyecto, en este orden:

git reset --hard
git pull
composer install --no-dev --optimize-autoloader
npm ci && npm run build
php artisan migrate --force
php artisan storage:link
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan queue:restart
Comando Qué hace
git reset --hard && git pull Descarta cualquier cosa editada en el servidor y toma el código nuevo. Lo que hayas cambiado a mano en el servidor se pierde, que es justo la idea
composer install --no-dev --optimize-autoloader Instala las dependencias PHP sin las de desarrollo (debugbar, IDE helper, Dusk, PHPUnit, el estándar de código). Corre package:discover y apunta core.hooksPath a scripts/git-hooks, lo cual es inofensivo en un servidor
npm ci && npm run build Instala las dependencias JavaScript exactas de package-lock.json y compila resources/css y resources/js en public/build/. Los archivos compilados no están committeados
php artisan migrate --force Corre las migraciones nuevas. --force se salta la confirmación que Laravel pide en producción
php artisan storage:link Crea public/storage apuntando a storage/app/public. Idempotente
php artisan optimize:clear Tira todos los cachés: configuración, rutas, vistas, eventos, clases compiladas. Hazlo antes de cachear otra vez para que nada viejo sobreviva
php artisan config:cache Fusiona config/*.php y .env en un solo archivo. Después de esto .env no se lee en absoluto: una variable cambiada sin volver a correr este comando se ignora
php artisan route:cache Cachea la tabla de rutas, closures incluidos, para que los archivos de rutas no se analicen en cada petición
php artisan view:cache Precompila las plantillas Blade
php artisan event:cache Cachea el descubrimiento de listeners. Los listeners de los registros de comunicación y de cobro viven en app/Listeners y se descubren, no se registran a mano
php artisan queue:restart Le dice a cada worker en marcha que termine su job actual y salga, para que Supervisor lo arranque otra vez con el código nuevo. Los workers conservan el código viejo en memoria hasta que esto corre

php artisan optimize corre los cuatro comandos de caché de una vez si lo prefieres.

Dos comandos del procedimiento anterior merecen una palabra. php artisan db:seed --force se puede repetir sin riesgo, porque cada seeder usa firstOrCreate, pero crea las cuentas listadas en sudo y default_users de config/app.php, y el default_users que viene de fábrica es [email protected] con una contraseña predecible. Quita esa entrada antes de sembrar una base de datos de producción. php artisan lang:search y php artisan lang:sync reescriben lang/*.json a través del proveedor de IA de config/ai.php; córrelos en tu máquina y committea el resultado, porque en el servidor el siguiente git reset --hard tira su salida.

La DevZone (/help/the-devzone) tiene un botón "Ejecutar despliegue" para versiones pequeñas: hace pull de la rama actual desde el remoto HTTPS con las credenciales de GIT_USER y GIT_PASSWORD, y luego corre los comandos que escribas en la caja, unidos con &&. Cada uno de los dos procesos se mata a los diez segundos, así que le cabe un pull más config:cache, no composer install ni npm run build. Una versión que toca dependencias o assets pasa por el script de arriba.

php artisan help:stamp --check corre en el hook pre-push y detiene el push cuando falta o está desactualizada una traducción de la ayuda. Es una revisión del repositorio, no un paso del despliegue; córrela en CI si tu pipeline hace push sin el hook.

El entorno de producción

.env.example está escrito para una máquina de desarrollo. Estos son los valores que cambian:

Variable Valor en producción Por qué
APP_ENV production Apaga el comportamiento exclusivo de local: el debugbar y el detector de consultas, y reportar cada excepción sin el límite de frecuencia
APP_DEBUG false Oculta las trazas a los visitantes. También es el interruptor que hace segura la cookie de sesión cuando APP_PWA está encendida
APP_URL https://your-project.com Se usa en cada enlace generado, el alcance del manifiesto, el sujeto VAPID y el dominio EHLO del correo por omisión
APP_KEY la que se generó una vez Cifra las sesiones y todo lo cifrado. Génerala una vez con php artisan key:generate --show, guárdala, nunca la regeneres en un sitio en marcha
QUEUE_CONNECTION database (por omisión) o redis Cualquier cosa menos sync. Con sync cada job corre dentro de la petición, y un aviso a mil usuarios bloquea al administrador que lo publicó
CACHE_STORE database (por omisión) o redis Nunca array en producción: queue:restart y el limitador de excepciones necesitan un caché que compartan el worker y el proceso web
SESSION_DRIVER database Lo que pone .env.example; la tabla sessions existe. El valor por omisión de la configuración es file
FILESYSTEM_DISK s3 con las variables AWS_*, o public El valor por omisión de la configuración es s3. Usa public para dejar las subidas en el disco del servidor
LOG_CHANNEL daily (por omisión) Un archivo por día en storage/logs/. Define SLACK_BOT_TOKEN y SLACK_LOG_CHANNEL y las excepciones también se publican en Slack, con límite de frecuencia: un error repetido se publica en la ocurrencia 1, 10, 25, 50, 100, 300, 500 y 1000 y cada mil después
MAIL_MAILER failover o un solo transporte failover intenta mailgun y luego ses, así que ambos necesitan sus llaves. Revisa /help/email-and-sms-delivery
MAIL_FROM_ADDRESS una dirección de tu dominio El remitente de cada correo; el valor por omisión de .env.example es de Weblabor
APP_PWA true cuando quieres la aplicación instalable y push Apagada, el manifiesto y el service worker no se inyectan. Revisa /help/web-push-and-the-pwa
VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY el par de php artisan webpush:vapid --show Obligatorias para web push. El mismo par en todos los lugares donde se suscriban los mismos usuarios
FEATURE_* las funciones que vendas Toda bandera viene apagada. Revisa /help/configure-your-project
STRIPE_KEY, STRIPE_SECRET, STRIPE_WEBHOOK_SECRET llaves live Sólo cuando planes o complementos están encendidos. El manejador de webhooks del proyecto responde en POST /api/stripe/webhook; registra esa URL en Stripe. Revisa /help/turn-on-plans-and-billing

Quita los marcadores con los que viene .env.example (your-aws-access-key-id, your-stripe-key, los ids de Telnyx): un marcador no está vacío, y el código que pregunta "¿esto está configurado?" creerá que sí.

El worker de la cola

Los avisos, las migraciones de plan y toda notificación que marques ShouldQueue corren en un worker. Arranca uno con Supervisor para que sobreviva caídas y reinicios. Crea /etc/supervisor/conf.d/your-project-worker.conf:

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

Luego:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start your-project-worker:*

--sleep=3 espera tres segundos cuando la cola está vacía, --tries=3 manda un job a failed_jobs tras su tercer fallo, y --max-time=3600 hace que el worker salga después de una hora para que Supervisor lo reinicie con memoria fresca. stopwaitsecs debe ser al menos tan largo como tu job más largo. Sube numprocs cuando un solo worker se quede atrás. Qué se encola, cómo vigilarlo y cómo reintentar fallos está en /help/queues-and-scheduled-work.

El programador

Dos tareas vienen programadas de fábrica: las estadísticas diarias de inicio de sesión a medianoche y la captura del tipo de cambio a mediodía (UTC, la zona horaria de la aplicación). Sólo corren si cron llama al programador cada minuto:

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

Agrégala al crontab del mismo usuario que es dueño de los archivos, para que los archivos de log que crea sigan siendo escribibles por el servidor web.

Los tres errores

Una bandera que nadie puso en el servidor. Toda función opcional es una variable de entorno que viene apagada. Una sección que funciona en tu máquina y falta en producción es una variable FEATURE_*, o APP_PWA, que se puso en tu .env y nunca en el del servidor. El panel de administración también pierde la sección, así que no es un problema de permisos.

Assets que nunca se compilaron. public/build/ no está committeado. Si la página carga sin estilos, o la consola del navegador reporta que falta /build/manifest.json, npm run build no corrió donde se sirve el sitio, o corrió con una versión de Node que Vite rechazó.

Un caché de configuración viejo. Una vez que corrió config:cache, .env es un archivo que nadie lee. Una variable que cambiaste y que la aplicación ignora es un caché construido antes del cambio: corre php artisan config:cache otra vez. Lo mismo aplica a las rutas después de agregar una, y a queue:restart después de cualquier despliegue, porque un worker que no se reinició corre el código de la semana pasada.