Saltar al contenido
Gontzal Bilbao
← cheatsheets

laravel · linux

Laravel en Producción: Colas, Supervisor y Logs

Comandos para operar Laravel en un servidor: workers con Supervisor, colas pendientes, logs, permisos de storage y caché de configuración.

13 secciones · 63 comandos · revisado 6 oct 2026

Tus valores

Cámbialos aquí y se aplican a todos los comandos. Se guardan en tu navegador.

Ábrela cuando un job no se procesa, un worker no recoge el código nuevo o tienes que revisar los logs en el servidor. Si no sabes por dónde empezar, ve a los escenarios del final: cada uno da el orden en que mirar.

1. Colas: inspeccionar y probar

Abre la consola interactiva de Laravel para consultar la configuración y las colas.

php artisan tinker

Lista las conexiones de cola registradas (dentro de tinker).

>>> array_keys(config('queue.connections'));

Muestra los jobs pendientes de la cola con el driver database (dentro de tinker).

>>> DB::table('jobs')->where('queue','emails')->get();

Encola un job de prueba para comprobar que el worker lo recoge (dentro de tinker).

>>> dispatch(new \App\Jobs\ExampleJob);

Cuenta los jobs que aún no se han intentado, consultando directamente la tabla jobs.

SELECT * FROM jobs WHERE queue = 'emails' AND attempts = 0;

Mide cuántos jobs esperan en la cola si usas el driver Redis.

redis-cli
> LLEN queues:emails

2. Workers con Supervisor

Supervisor mantiene vivos los procesos queue:work. Tras tocar la configuración, el ciclo es reread, update y restart.

Abre la definición del programa de Supervisor para editarla.

sudo nano /etc/supervisor/conf.d/app-workers.conf

Recarga la configuración y aplica los cambios a los procesos.

sudo supervisorctl reread
sudo supervisorctl update

Comprueba el estado de todos los workers del programa.

sudo supervisorctl status app-workers:*

Reinicia todos los workers del programa, por ejemplo tras un despliegue.

sudo supervisorctl restart app-workers:*

Tras cada despliegue, reinicia los workers: ejecutan el código que cargaron en memoria al arrancar, así que sin reinicio siguen con la versión anterior.

3. Seguir logs en producción

Sigue en tiempo real el log de los workers.

tail -f /var/www/app/current/storage/logs/worker.log

Sigue en tiempo real el log de la aplicación.

tail -f /var/www/app/current/storage/logs/laravel.log

Sigue un log con less para poder buscar con /patrón; Ctrl-C para el seguimiento.

less +F /var/www/app/current/storage/logs/worker.log

Muestra las últimas 100 líneas de un log.

tail -n 100 /var/www/app/current/storage/logs/worker.log

Sigue varios logs a la vez en la misma pantalla.

multitail \
  /var/www/app/current/storage/logs/worker.log \
  /var/www/app/current/storage/logs/laravel.log

Navega por todos los logs con resaltado y filtros.

lnav /var/www/app/current/storage/logs/*.log

4. Filtrar logs

Sigue el log mostrando solo las líneas de un job concreto; --line-buffered hace que grep no retenga la salida.

tail -f /var/www/app/current/storage/logs/worker.log \
  | grep --line-buffered 'ExampleJob'

Sigue solo las líneas de un job en estado RUNNING o DONE.

tail -f /var/www/app/current/storage/logs/worker.log \
  | grep --line-buffered -E 'ExampleJob.*(RUNNING|DONE)'

Busca errores en todos los logs, con número de línea.

grep -R "ERROR" -n /var/www/app/current/storage/logs

Cuenta cuántas veces aparece un job en el log.

grep -c "ExampleJob" /var/www/app/current/storage/logs/worker.log

Filtra las líneas de un día concreto.

grep "2026-10-06" /var/www/app/current/storage/logs/worker.log

5. Tamaño, limpieza y rotación de logs

Mide cuánto ocupa la carpeta de logs.

du -sh /var/www/app/current/storage/logs

Lista los logs ordenados por tamaño.

ls -lh /var/www/app/current/storage/logs | sort -k5 -h

Vacía un log sin borrar el fichero, conservando su inodo y sus permisos.

truncate -s 0 /var/www/app/current/storage/logs/worker.log

Revisa cómo rota logrotate los logs de la aplicación.

cat /etc/logrotate.d/laravel

6. Permisos de storage y bootstrap/cache

El fallo más común tras un despliegue: el servidor web no puede escribir en storage o bootstrap/cache.

Comprueba el propietario y los permisos de storage y de su carpeta de logs.

ls -ld /var/www/app/current/storage
ls -ld /var/www/app/current/storage/logs

Asigna storage y bootstrap/cache al usuario del servidor web.

sudo chown -R www-data:www-data \
  /var/www/app/current/storage/ \
  /var/www/app/current/bootstrap/cache

Da permisos 775 a todos los directorios de storage y bootstrap/cache.

find /var/www/app/current/storage/ /var/www/app/current/bootstrap/cache -type d -exec chmod 775 {} \;

Da permisos 664 a todos los ficheros de storage y bootstrap/cache.

find /var/www/app/current/storage/ /var/www/app/current/bootstrap/cache -type f -exec chmod 664 {} \;

7. Caché de configuración

Regenera la caché de configuración tras cambiar .env o config/*.

php artisan config:clear
php artisan config:cache

Cachea las rutas para que no se resuelvan en cada petición.

php artisan route:cache

Precompila las vistas Blade.

php artisan view:cache

Tras config:cache, Laravel deja de leer .env en tiempo de ejecución y usa la caché. Si cambias una variable de entorno y no ves el efecto, regenera la caché.

8. Inventariar jobs y colas en el código

Antes de tocar Supervisor conviene saber qué colas existen y cómo están configuradas. Estos comandos se lanzan desde la raíz del proyecto.

Lista todos los ficheros de job.

find app -type f -path '*/Jobs/*.php'

Muestra la cola que declara cada job.

grep -R "public \$queue" -n app/Jobs

Muestra el timeout que declara cada job.

grep -R "public \$timeout" -n app/Jobs

Lista los jobs que se encolan, es decir, los que implementan ShouldQueue.

grep -R "implements ShouldQueue" -n app/Jobs

Encuentra dónde se asigna la cola en el momento de despachar.

grep -R "onQueue" -n app

9. Escenario: los jobs se quedan en la cola

situaciónLos jobs se encolan pero nadie los procesa: la cola crece y los correos no salen.

qué hay que entender

Un worker queue:work es un proceso largo: arranca, carga la aplicación en memoria y escucha una conexión y unas colas concretas. Supervisor lo mantiene vivo. Si el worker está caído, colgado o escucha otra cola u otra conexión, los jobs esperan sin que nada falle.

Como el worker no relee el código, tras un despliegue sigue con la versión anterior. queue:restart le pide que termine el job en curso y salga, y Supervisor lo vuelve a levantar con el código nuevo.

pasos, en orden

  1. Mira si los workers están en marcha: RUNNING está bien; FATAL, BACKOFF o STOPPED, no.

    sudo supervisorctl status app-workers:*
  2. Revisa qué conexión y qué colas escucha el worker, en la línea command del programa.

    grep '^command' /etc/supervisor/conf.d/app-workers.conf
  3. Comprueba a qué conexión envía los jobs la aplicación: debe coincidir con la del worker.

    grep '^QUEUE_CONNECTION' /var/www/app/current/.env
  4. Procesa un solo job en primer plano, con el usuario del servidor web, para ver el error en directo.

    sudo -u www-data php /var/www/app/current/artisan queue:work --queue=emails --once
  5. Si los workers están caídos o colgados, reinícialos desde Supervisor.

    sudo supervisorctl restart app-workers:*
  6. Tras cada despliegue, pide a los workers que se reinicien para que carguen el código nuevo.

    php /var/www/app/current/artisan queue:restart

cómo sé que está resuelto

supervisorctl status muestra todos los procesos en RUNNING y la cola baja: con Redis, LLEN queues:emails tiende a 0. Un job de prueba encolado desde tinker aparece en el log de los workers.

si no funciona

Si un worker pasa a FATAL al poco de arrancar, lee su salida con sudo supervisorctl tail -f seguido del nombre del proceso que da status. Si queue:restart no tiene efecto, revisa el driver de caché: la señal se guarda en la caché, y con array no llega a los workers. Y si cambiaste .env con la configuración cacheada, regenera config:cache.

en una entrevista«Los jobs de una cola no se procesan. ¿Qué miras?»
  1. Hay workers vivos: supervisorctl status, porque sin proceso no hay nada más que mirar.
  2. Escuchan lo que toca: comparo la conexión y la cola del worker con las que usa la aplicación.
  3. Están procesando: un queue:work --once en primer plano me enseña el error sin intermediarios.
  4. Llevan el código nuevo: si viene de un despliegue, queue:restart forma parte del propio despliegue.

La trampa que conviene nombrar: un worker sin reiniciar tras desplegar sigue ejecutando el código viejo, y el fallo parece del código nuevo.

10. Escenario: los jobs fallan una y otra vez

situaciónLos jobs se recogen pero acaban en «failed», o se ejecutan dos veces.

qué hay que entender

Un job se intenta tantas veces como diga --tries o el propio job. Al agotar los intentos pasa a la tabla failed_jobs, con la excepción completa. Ahí está el motivo, no en la cola.

Hay dos tiempos que se confunden. El --timeout del worker mata un job que tarda demasiado. El retry_after de la conexión dice cuántos segundos espera la cola antes de dar por perdido un job en curso y entregarlo otra vez. Si retry_after es menor que --timeout, un job lento se ejecuta dos veces.

pasos, en orden

  1. Lista los jobs fallidos con su cola, su clase y cuándo fallaron.

    php /var/www/app/current/artisan queue:failed
  2. Lee la excepción de los últimos fallos, directamente en la tabla.

    SELECT id, queue, failed_at, LEFT(exception, 300) AS error
    FROM failed_jobs ORDER BY failed_at DESC LIMIT 5;
  3. Mira el retry_after de cada conexión de cola.

    grep -n "retry_after" /var/www/app/current/config/queue.php
  4. Compáralo con el --timeout del worker: el timeout tiene que ser varios segundos menor.

    grep -o -- '--timeout=[0-9]*' /etc/supervisor/conf.d/app-workers.conf
  5. Con la causa corregida, desplegada y los workers reiniciados, reintenta los jobs fallidos.

    php /var/www/app/current/artisan queue:retry all

cómo sé que está resuelto

queue:failed queda vacío o deja de crecer, y en el log de los workers los jobs reintentados aparecen como DONE.

si no funciona

Si el fallo solo ocurre a veces, busca la clase en el log con contexto: grep -A 20 'ExampleJob' sobre laravel.log. Para reintentar solo uno, pasa su ID a queue:retry. Un job que no tiene sentido repetir se descarta con queue:forget y su ID. Si se ejecuta dos veces aunque no falle, vuelve al paso 4.

en una entrevista«Un job falla una y otra vez. ¿Cómo lo investigas?»
  1. El motivo está en failed_jobs: leo la excepción antes de tocar nada.
  2. Clasifico el fallo: error del código, dato que falta o dependencia externa que no responde.
  3. Reviso los tiempos: --timeout del worker por debajo de retry_after de la conexión.
  4. Corrijo, despliego y reinicio los workers antes de reintentar.
  5. Reintento con cuidado: solo si el job es idempotente, es decir, si repetirlo no duplica efectos.

Lo que no haría: un queue:retry all antes de corregir la causa, que vuelve a llenar la tabla de fallos.

11. Escenario: error 500 justo después de desplegar

situaciónTras un despliegue, la aplicación devuelve «500 Server Error» en todas las páginas.

qué hay que entender

En producción, con APP_DEBUG=false, Laravel enseña un 500 genérico y escribe el error real en storage/logs/laravel.log. Ese fichero es lo primero, porque dice la causa exacta.

Tras un despliegue, las causas típicas son cuatro. Una caché de configuración o de rutas con datos de la versión anterior. Permisos que impiden escribir en storage o bootstrap/cache. Migraciones sin aplicar. O una variable de .env que falta.

pasos, en orden

  1. Lee las últimas líneas del log de la aplicación.

    tail -n 50 /var/www/app/current/storage/logs/laravel.log
  2. Comprueba que storage y bootstrap/cache pertenecen al usuario del servidor web.

    ls -ld /var/www/app/current/storage /var/www/app/current/bootstrap/cache
  3. Si no es así, devuélveselos.

    sudo chown -R www-data:www-data \
      /var/www/app/current/storage \
      /var/www/app/current/bootstrap/cache
  4. Borra las cachés de configuración, rutas y vistas con ese mismo usuario, para no dejar ficheros de root.

    sudo -u www-data php /var/www/app/current/artisan optimize:clear
  5. Mira si quedan migraciones sin aplicar.

    php /var/www/app/current/artisan migrate:status
  6. Aplícalas; --force es obligatorio en producción, donde Laravel pide confirmación.

    php /var/www/app/current/artisan migrate --force

cómo sé que está resuelto

La aplicación responde, por ejemplo curl -I a la portada devuelve 200, y laravel.log deja de crecer con errores. Después, regenera config:cache y route:cache con el usuario del servidor web.

si no funciona

Si laravel.log no tiene nada, Laravel no llegó a arrancar o no puede escribir: mira el log de errores del servidor web, como /var/log/nginx/error.log, y el de PHP-FPM. Si el error nombra una variable o una clave vacía, compara .env con el de la versión anterior. Si nada lo explica pronto, vuelve a la versión anterior y sigue sin la web caída.

en una entrevista«Acabas de desplegar y la web da un 500. ¿Qué haces?»
  1. El log primero: laravel.log dice la causa; sin él estoy adivinando.
  2. Qué ha cambiado: código, .env, dependencias o esquema de base de datos.
  3. Causas típicas, en orden: caché vieja, permisos de storage y bootstrap/cache, migraciones y entorno.
  4. Artisan con el usuario correcto: si lo lanzo como root, las cachés nuevas quedan de root y el 500 vuelve.
  5. Si tarda, vuelvo atrás: una versión anterior que funciona vale más que diagnosticar en caliente.

12. Escenario: el log crece sin parar y llena el disco

situaciónEl fichero laravel.log ocupa gigas y el servidor se queda sin disco.

qué hay que entender

Con el canal single, Laravel escribe siempre en el mismo laravel.log y nunca lo recorta. El canal daily crea un fichero por día y borra los más antiguos que los días configurados. La otra opción es logrotate, pero no las dos sobre el mismo fichero.

El nivel también cuenta: con LOG_LEVEL=debug en producción se escribe todo. Y un worker es un proceso largo que mantiene abierto el log, así que borrarlo no libera el espacio hasta reiniciarlo; vaciarlo, sí.

pasos, en orden

  1. Mira qué logs ocupan más, ordenados por tamaño.

    ls -lhS /var/www/app/current/storage/logs | head -n 10
  2. Comprueba el canal y el nivel de log configurados.

    grep -E '^LOG_(CHANNEL|LEVEL)' /var/www/app/current/.env
  3. Libera espacio ya vaciando el log en vez de borrarlo, para que los procesos que lo tienen abierto lo suelten.

    truncate -s 0 /var/www/app/current/storage/logs/laravel.log
  4. Pon LOG_CHANNEL=daily y LOG_LEVEL=warning en .env, y regenera la caché para que se aplique.

    sudo -u www-data php /var/www/app/current/artisan config:cache
  5. Si prefieres logrotate, simula su configuración sin rotar nada para ver qué haría.

    sudo logrotate -d /etc/logrotate.d/laravel

cómo sé que está resuelto

df -h vuelve a dejar margen, aparece un laravel- con la fecha del día en storage/logs y el volumen de escritura baja al quitar el nivel debug.

si no funciona

Si el que crece es worker.log y lo escribe Supervisor, la rotación se configura en el programa con stdout_logfile_maxbytes y stdout_logfile_backups. Si el espacio no aparece, revisa los releases antiguos y su storage. Para el resto del diagnóstico, sigue el escenario «el disco está lleno» de Linux & Sysadmin.

en una entrevista«El log de Laravel ha llenado el disco. ¿Cómo lo resuelves y cómo evitas que vuelva?»
  1. Contener: vacío el log con truncate, no lo borro, porque los procesos lo tienen abierto.
  2. Causa: un único fichero sin rotar, un nivel debug en producción o un error que se repite en bucle.
  3. Rotación: canal daily con retención, o logrotate, pero no los dos sobre el mismo fichero.
  4. Prevenir: nivel warning o superior en producción y una alarma de disco antes del 100 %.

La trampa que conviene nombrar: si el log crece por un error repetido, rotar solo esconde el problema.

13. Relacionado