Saltar al contenido principal

Qué es Horizon

Laravel Horizon es un panel de monitorización específico para las colas de Redis de Laravel. Visualiza en tiempo real el rendimiento, el tiempo de ejecución y las incidencias de los jobs, y te permite gestionar la configuración de los workers como código.
Horizon es un paquete que extiende las capacidades básicas de las colas. Familiarízate primero con las colas y jobs antes de continuar. Además, necesitas Redis como backend obligatorio.

Instalación

Horizon utiliza Redis como backend de las colas. Comprueba que QUEUE_CONNECTION en config/queue.php está fijado a redis. Actualmente no admite Redis Cluster.
Instala con Composer.
composer require laravel/horizon
Después publica los assets y el archivo de configuración de Horizon.
php artisan horizon:install
Este comando genera config/horizon.php y app/Providers/HorizonServiceProvider.php.

Configuración

Estructura de config/horizon.php

config/horizon.php centraliza toda la configuración de los workers. La sección más importante es environments.
'environments' => [
    'production' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => ['default', 'notifications'],
            'balance' => 'auto',
            'autoScalingStrategy' => 'time',
            'minProcesses' => 1,
            'maxProcesses' => 10,
            'balanceMaxShift' => 1,
            'balanceCooldown' => 3,
            'tries' => 3,
            'timeout' => 60,
        ],
    ],

    'local' => [
        'supervisor-1' => [
            // El resto de valores se hereda de la sección defaults
            'maxProcesses' => 3,
        ],
    ],
],
Internamente Horizon utiliza una conexión de Redis llamada horizon. No reutilices ese nombre en config/database.php para otras conexiones.

Supervisores

Cada entorno puede tener uno o varios «supervisores». Un supervisor es la unidad de gestión de un grupo de workers; puedes ejecutar varios en el mismo entorno con distintas colas, estrategias de balanceo y número de procesos.

Valores por defecto

La opción defaults establece los valores que se aplican a todos los supervisores por defecto.
'defaults' => [
    'supervisor-1' => [
        'connection' => 'redis',
        'queue' => ['default'],
        'balance' => 'auto',
        'tries' => 1,
        'timeout' => 60,
        'maxProcesses' => 1,
    ],
],

Modo mantenimiento

Cuando la aplicación está en modo mantenimiento, Horizon no procesa jobs por defecto. Para forzarlo, usa la opción force.
'environments' => [
    'production' => [
        'supervisor-1' => [
            'force' => true,
        ],
    ],
],

Máximo de reintentos

'environments' => [
    'production' => [
        'supervisor-1' => [
            'tries' => 10,
        ],
    ],
],
Con tries a 0 se permiten reintentos ilimitados.

Timeout de los jobs

'environments' => [
    'production' => [
        'supervisor-1' => [
            'timeout' => 60,
        ],
    ],
],
El valor de timeout debe ser unos segundos inferior a retry_after de config/queue.php. Además, con la estrategia auto los jobs que superen ese tiempo pueden abortarse.

Backoff (espera antes de reintentar)

Especifica los segundos que hay que esperar antes de reintentar tras una excepción.
// Valor fijo
'backoff' => 10,

// Escalonado (backoff exponencial)
'backoff' => [1, 5, 10],

Estrategias de balanceo

Horizon dispone de tres estrategias de balanceo de workers.
Ajusta automáticamente el número de workers según la carga de la cola. Con minProcesses y maxProcesses defines el rango.
'supervisor-1' => [
    'balance' => 'auto',
    'autoScalingStrategy' => 'time', // o 'size'
    'minProcesses' => 1,
    'maxProcesses' => 10,
    'balanceMaxShift' => 1,
    'balanceCooldown' => 3,
],
  • time: escala en función del tiempo estimado para vaciar la cola.
  • size: escala en función del número de jobs en la cola.
Con la estrategia auto el orden de las colas no implica prioridad. Si necesitas forzar prioridades, utiliza varios supervisores.
Fija el número de workers y los reparte a partes iguales entre las colas indicadas.
'supervisor-1' => [
    'balance' => 'simple',
    'processes' => 10,
    'queue' => ['default', 'notifications'],
],
En este ejemplo se asignan 5 procesos a default y 5 a notifications.
Prioriza estrictamente las colas en el orden en el que se enumeran. Se comporta igual que el sistema de colas por defecto de Laravel, pero escala el número de workers en función del backlog.
'supervisor-1' => [
    'balance' => false,
    'queue' => ['default', 'notifications'],
    'minProcesses' => 1,
    'maxProcesses' => 10,
],
Los jobs de la cola default se procesarán siempre antes que los de notifications.

Autorización del panel

El panel de Horizon está disponible en la ruta /horizon. En entornos locales cualquiera puede acceder por defecto, pero en producción debes restringir el acceso mediante un gate. Edita el método gate() en app/Providers/HorizonServiceProvider.php.
use App\Models\User;
use Illuminate\Support\Facades\Gate;

protected function gate(): void
{
    Gate::define('viewHorizon', function (User $user) {
        return in_array($user->email, [
            '[email protected]',
        ]);
    });
}
Si no necesitas autenticación (porque proteges el acceso por IP u otro medio), marca el argumento como opcional.
Gate::define('viewHorizon', function (User $user = null) {
    // Cuando el acceso está restringido por IP u otro método
    return true;
});

Arrancar Horizon

Comandos básicos

# Arrancar
php artisan horizon

# Pausar / reanudar
php artisan horizon:pause
php artisan horizon:continue

# Pausar / reanudar un supervisor concreto
php artisan horizon:pause-supervisor supervisor-1
php artisan horizon:continue-supervisor supervisor-1

# Estado
php artisan horizon:status
php artisan horizon:supervisor-status supervisor-1

# Apagado limpio
php artisan horizon:terminate

Desarrollo local: reinicio automático

Para reiniciar Horizon automáticamente cuando cambien los archivos, utiliza horizon:listen.
npm install --save-dev chokidar
php artisan horizon:listen

# En entornos Docker o Vagrant
php artisan horizon:listen --poll

Ejecución permanente con Supervisor

En producción utiliza Supervisor para mantener Horizon siempre en ejecución.

Instalar Supervisor

sudo apt-get install supervisor

Crear el archivo de configuración

Crea /etc/supervisor/conf.d/horizon.conf.
[program:horizon]
process_name=%(program_name)s
command=php /home/forge/example.com/artisan horizon
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/horizon.log
stopwaitsecs=3600
Asigna a stopwaitsecs un valor mayor que la duración del job más largo. Si es demasiado pequeño, Supervisor abortará los jobs a mitad de ejecución.

Arrancar Supervisor

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon

Al desplegar

Reinicia Horizon en cada despliegue para que se apliquen los cambios.
php artisan horizon:terminate
Si Supervisor está configurado con autostart=true y autorestart=true, Horizon volverá a arrancar automáticamente tras terminar.

Gestión de jobs

Etiquetas

Horizon detecta automáticamente los modelos Eloquent que reciben los jobs y les asigna etiquetas.
// Un job que recibe el modelo Video (id=1) recibe automáticamente la etiqueta "App\Models\Video:1"
RenderVideo::dispatch(Video::find(1));
Para definir etiquetas manualmente, implementa el método tags().
class RenderVideo implements ShouldQueue
{
    /**
     * @return array<int, string>
     */
    public function tags(): array
    {
        return ['render', 'video:'.$this->video->id];
    }
}
En los listeners de eventos, la instancia del evento se pasa como argumento a tags().
class SendRenderNotifications implements ShouldQueue
{
    public function tags(VideoRendered $event): array
    {
        return ['video:'.$event->video->id];
    }
}

Silenciar jobs

Los jobs que no quieras que aparezcan en la lista de «Completed Jobs» pueden silenciarse en config/horizon.php.
'silenced' => [
    App\Jobs\ProcessPodcast::class,
],

// Silenciar por etiqueta
'silenced_tags' => [
    'notifications',
],
También puedes implementar la interfaz Silenced.
use Laravel\Horizon\Contracts\Silenced;

class ProcessPodcast implements ShouldQueue, Silenced
{
    use Queueable;
    // ...
}

Métricas y monitorización

El panel de métricas de Horizon muestra el rendimiento y los tiempos de ejecución de jobs y colas. Programa un job periódico para capturar snapshots.
// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('horizon:snapshot')->everyFiveMinutes();
Para eliminar todas las métricas ejecuta:
php artisan horizon:clear-metrics

Notificaciones ante jobs fallidos

Puedes recibir notificaciones cuando las esperas en cola se prolonguen. Configúralo en el método boot() de app/Providers/HorizonServiceProvider.php.
use Laravel\Horizon\Horizon;

public function boot(): void
{
    parent::boot();

    Horizon::routeMailNotificationsTo('[email protected]');
    Horizon::routeSlackNotificationsTo('slack-webhook-url', '#ops');
    Horizon::routeSmsNotificationsTo('15556667777');
}

Umbrales de espera

La opción waits de config/horizon.php define los segundos de espera que disparan la notificación.
'waits' => [
    'redis:critical' => 30,  // Notifica si se espera 30 s o más
    'redis:default' => 60,
    'redis:batch' => 120,
],
Un valor de 0 deshabilita las notificaciones para esa cola.

Gestión de jobs fallidos

Los jobs fallidos se eliminan por ID o UUID.
# Elimina un job fallido concreto
php artisan horizon:forget 5

# Elimina todos los jobs fallidos
php artisan horizon:forget --all
Para vaciar todos los jobs de una cola:
# Vacía la cola por defecto
php artisan horizon:clear

# Vacía una cola concreta
php artisan horizon:clear --queue=emails

Actualizaciones

Cuando actualices Horizon a una versión mayor, revisa siempre la guía de actualización.

Páginas relacionadas

Colas y jobs

Fundamentos de las colas de Laravel: creación y despacho de jobs, procesamiento por lotes y gestión de fallos.

Redis

Configuración y uso de Redis, backend obligatorio de Horizon.
Última modificación el 13 de julio de 2026