> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Horizon

> How to visually manage and monitor Redis queues with Laravel Horizon. Covers the dashboard, balance strategies, Supervisor configuration, and notifications.

## What is Horizon?

[Laravel Horizon](https://github.com/laravel/horizon) is a monitoring dashboard dedicated to **Laravel's Redis queue**. You can visualize job throughput, run time, and failure state in real time and manage your worker configuration in code.

<Info>
  Horizon is a package that extends the base queue features. Make sure you understand the fundamentals of [queues and jobs](/en/queues) before reading on. You must also use [Redis](/en/redis) as the backend.
</Info>

```mermaid theme={null}
flowchart LR
    Browser["Browser"] -->|"/horizon"| Dashboard["Horizon<br>dashboard"]
    Dashboard -->|"Monitor / control"| Horizon["Horizon<br>process"]
    Horizon -->|"Job management"| Redis["Redis<br>Queue"]
    Redis -->|"Fetch jobs"| Worker1["Worker 1"]
    Redis -->|"Fetch jobs"| Worker2["Worker 2"]
    Redis -->|"Fetch jobs"| Worker3["Worker 3"]
```

## Installation

<Warning>
  Horizon uses Redis as its queue backend. Make sure `QUEUE_CONNECTION` in `config/queue.php` is set to `redis`. Redis Cluster is not currently supported.
</Warning>

Install via Composer.

```shell theme={null}
composer require laravel/horizon
```

After installation, publish Horizon's assets and configuration files.

```shell theme={null}
php artisan horizon:install
```

This command generates `config/horizon.php` and `app/Providers/HorizonServiceProvider.php`.

## Configuration

### Structure of config/horizon.php

`config/horizon.php` is the file that manages all worker configuration. The core option is `environments`.

```php theme={null}
'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' => [
            // Other values are inherited from the defaults section
            'maxProcesses' => 3,
        ],
    ],
],
```

<Info>
  Horizon internally uses a Redis connection named `horizon`. Do not use this name for any other connection in `config/database.php`.
</Info>

### CSP nonce (Content Security Policy)

If you want to set a [nonce attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) on the `script` / `style` tags used in Horizon's views as part of a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), use the `Horizon::cspNonce` method. Since a new nonce is assigned per request, you'll typically call this inside a middleware.

```php theme={null}
use Closure;
use Illuminate\Http\Request;
use Laravel\Horizon\Horizon;
use Symfony\Component\HttpFoundation\Response;

public function handle(Request $request, Closure $next): Response
{
    Horizon::cspNonce('csp-nonce');

    return $next($request);
}
```

Add this middleware to the `middleware` option in `config/horizon.php`.

```php theme={null}
'middleware' => [
    'web',
    App\Http\Middleware\AddHorizonCspNonce::class,
],
```

### Supervisors

Each environment can have one or more "supervisors". A supervisor is a management unit for a worker group; you can run multiple supervisors with different queues, balance strategies, and process counts inside the same environment.

### Default values

Use the `defaults` option to configure default values that apply to every supervisor.

```php theme={null}
'defaults' => [
    'supervisor-1' => [
        'connection' => 'redis',
        'queue' => ['default'],
        'balance' => 'auto',
        'tries' => 1,
        'timeout' => 60,
        'maxProcesses' => 1,
    ],
],
```

### Maintenance mode

When your application is in maintenance mode, Horizon does not process jobs by default. To force processing, use the `force` option.

```php theme={null}
'environments' => [
    'production' => [
        'supervisor-1' => [
            'force' => true,
        ],
    ],
],
```

### Maximum number of attempts

```php theme={null}
'environments' => [
    'production' => [
        'supervisor-1' => [
            'tries' => 10,
        ],
    ],
],
```

Setting `tries` to `0` allows unlimited retries.

### Job timeout

```php theme={null}
'environments' => [
    'production' => [
        'supervisor-1' => [
            'timeout' => 60,
        ],
    ],
],
```

<Warning>
  Set `timeout` to a value a few seconds shorter than `retry_after` in `config/queue.php`. Also, the `auto` balance strategy may forcibly terminate jobs that run longer than this value.
</Warning>

### Backoff (wait time before retry)

Specify the number of seconds to wait before retrying after an exception.

```php theme={null}
// Fixed value
'backoff' => 10,

// Progressive (exponential backoff)
'backoff' => [1, 5, 10],
```

### Other worker options

In addition to `tries`, `timeout`, and `backoff`, each supervisor accepts options that control worker process behavior and when workers should automatically restart. Periodically restarting long-running processes is a good practice for preventing memory leaks.

```php theme={null}
'environments' => [
    'production' => [
        'supervisor-1' => [
            // ...
            'memory' => 128,
            'maxJobs' => 1000,
            'maxTime' => 3600,
            'sleep' => 3,
            'rest' => 0,
            'nice' => 0,
        ],
    ],
],
```

* `memory` — the maximum memory (MB) a worker process can consume before restarting. Defaults to `128`.
* `maxJobs` — the number of jobs to process before restarting. `0` means unlimited. Defaults to `0`.
* `maxTime` — the number of seconds a worker can run before restarting. `0` means no time-based restart. Defaults to `0`.
* `sleep` — seconds to wait before polling for the next job when the queue is empty. Defaults to `3`.
* `rest` — seconds to pause between each job. Defaults to `0`.
* `nice` — the priority ("niceness") of the worker process. Higher values mean lower priority. Defaults to `0`.

## Balance strategies

Horizon provides three worker balance strategies.

<AccordionGroup>
  <Accordion title="auto (default)">
    Automatically adjusts the number of workers based on queue load. Use `minProcesses` and `maxProcesses` to define the range.

    ```php theme={null}
    'supervisor-1' => [
        'balance' => 'auto',
        'autoScalingStrategy' => 'time', // or 'size' / 'log'
        'minProcesses' => 1,
        'maxProcesses' => 10,
        'balanceMaxShift' => 1,
        'balanceCooldown' => 3,
    ],
    ```

    * `time` — scales based on the estimated time to drain the queue.
    * `size` — scales based on the number of jobs in the queue.
    * `log` — scales based on the logarithm of the number of jobs in the queue. Prevents workers from being concentrated on a specific queue even when the queue grows significantly.

    <Info>
      Under the `auto` strategy, queue order does not indicate priority. If you need to enforce priority, use multiple supervisors.
    </Info>
  </Accordion>

  <Accordion title="simple">
    Uses a fixed number of workers and distributes them evenly across the configured queues.

    ```php theme={null}
    'supervisor-1' => [
        'balance' => 'simple',
        'processes' => 10,
        'queue' => ['default', 'notifications'],
    ],
    ```

    In the example above, five processes each are assigned to `default` and `notifications`.
  </Accordion>

  <Accordion title="false (no balancing)">
    Strictly prioritizes queues in the order listed. Behavior is similar to Laravel's default queue system, but Horizon scales worker count based on backlog.

    ```php theme={null}
    'supervisor-1' => [
        'balance' => false,
        'queue' => ['default', 'notifications'],
        'minProcesses' => 1,
        'maxProcesses' => 10,
    ],
    ```

    Jobs on the `default` queue are always processed before jobs on `notifications`.
  </Accordion>
</AccordionGroup>

## Dashboard authorization

Horizon's dashboard is accessible at the `/horizon` route. In the local environment, anyone can access it by default, but in **production** you should restrict access via a gate.

Edit the `gate()` method in `app/Providers/HorizonServiceProvider.php`.

```php theme={null}
use App\Models\User;
use Illuminate\Support\Facades\Gate;

protected function gate(): void
{
    Gate::define('viewHorizon', function (User $user) {
        return in_array($user->email, [
            'admin@example.com',
        ]);
    });
}
```

If authentication isn't required (for example, when you protect it via IP restriction), make the argument optional.

```php theme={null}
Gate::define('viewHorizon', function (User $user = null) {
    // When you restrict access by IP or similar
    return true;
});
```

## Starting Horizon

### Basic commands

```shell theme={null}
# Start
php artisan horizon

# Pause / resume
php artisan horizon:pause
php artisan horizon:continue

# Pause / resume a specific supervisor
php artisan horizon:pause-supervisor supervisor-1
php artisan horizon:continue-supervisor supervisor-1

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

# Graceful shutdown
php artisan horizon:terminate
```

### Local development: auto restart

Use the `horizon:listen` command to detect file changes and automatically restart Horizon.

```shell theme={null}
npm install --save-dev chokidar
php artisan horizon:listen

# In Docker or Vagrant environments
php artisan horizon:listen --poll
```

### Continuous operation with Supervisor

In production, use Supervisor to keep Horizon running.

#### Installing Supervisor

```shell theme={null}
sudo apt-get install supervisor
```

#### Creating the config file

Create `/etc/supervisor/conf.d/horizon.conf`.

```ini theme={null}
[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
```

<Warning>
  Set `stopwaitsecs` to a value larger than your longest-running job. Setting it too low will cause Supervisor to forcibly terminate jobs mid-flight.
</Warning>

#### Starting Supervisor

```shell theme={null}
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start horizon
```

#### On deploy

Restart Horizon on every code deploy so your changes take effect.

```shell theme={null}
php artisan horizon:terminate
```

As long as Supervisor is configured with `autostart=true` / `autorestart=true`, it will restart Horizon automatically after termination.

## Managing jobs

### Tags

Horizon automatically detects Eloquent models related to a job and adds them as tags.

```php theme={null}
// A job that receives a Video model (id=1) → automatically tagged "App\Models\Video:1"
RenderVideo::dispatch(Video::find(1));
```

To define tags manually, implement the `tags()` method.

```php theme={null}
class RenderVideo implements ShouldQueue
{
    /**
     * @return array<int, string>
     */
    public function tags(): array
    {
        return ['render', 'video:'.$this->video->id];
    }
}
```

For event listeners, the event instance is passed to `tags()`.

```php theme={null}
class SendRenderNotifications implements ShouldQueue
{
    public function tags(VideoRendered $event): array
    {
        return ['video:'.$event->video->id];
    }
}
```

### Silencing

You can silence jobs you don't want shown in the "Completed Jobs" list on the dashboard by configuring `config/horizon.php`.

```php theme={null}
'silenced' => [
    App\Jobs\ProcessPodcast::class,
],

// Silence by tag
'silenced_tags' => [
    'notifications',
],
```

You can also implement the `Silenced` interface.

```php theme={null}
use Laravel\Horizon\Contracts\Silenced;

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

## Metrics and monitoring

Horizon's metrics dashboard shows job and queue throughput and run time. Schedule regular snapshots to keep the data up to date.

```php theme={null}
// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('horizon:snapshot')->everyFiveMinutes();
```

The `metrics.trim_snapshots` option in `config/horizon.php` controls how many snapshots to keep for the metrics graphs. This setting limits by count rather than age, so the actual retention period depends on how often you run `horizon:snapshot`.

```php theme={null}
'metrics' => [
    'trim_snapshots' => [
        'job' => 24,
        'queue' => 24,
    ],
],
```

To delete all metrics data, run:

```shell theme={null}
php artisan horizon:clear-metrics
```

## Notifications on job failure

You can be notified when queue wait times grow long. Configure this in the `boot()` method of `app/Providers/HorizonServiceProvider.php`.

```php theme={null}
use Laravel\Horizon\Horizon;

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

    Horizon::routeMailNotificationsTo('admin@example.com');
    Horizon::routeSlackNotificationsTo('slack-webhook-url', '#ops');
    Horizon::routeSmsNotificationsTo('15556667777');
}
```

### Wait time thresholds

Configure the seconds of waiting that trigger notifications with the `waits` option in `config/horizon.php`.

```php theme={null}
'waits' => [
    'redis:critical' => 30,  // Notify after waiting 30 seconds or more
    'redis:default' => 60,
    'redis:batch' => 120,
],
```

Setting a value to `0` disables notifications for that queue.

## Managing failed jobs

You can delete failed jobs by ID or UUID.

```shell theme={null}
# Delete a specific failed job
php artisan horizon:forget 5

# Delete every failed job
php artisan horizon:forget --all
```

To clear every job on a queue, use:

```shell theme={null}
# Clear the default queue
php artisan horizon:clear

# Clear a specific queue
php artisan horizon:clear --queue=emails
```

## Upgrading

Always check the [upgrade guide](https://github.com/laravel/horizon/blob/master/UPGRADE.md) when upgrading Horizon to a major version.

## Related pages

<CardGroup cols={2}>
  <Card title="Queues and jobs" href="/en/queues">
    The basics of Laravel queues — creating and dispatching jobs, batching, and handling failures.
  </Card>

  <Card title="Redis" href="/en/redis">
    Configuring and using the Redis backend that Horizon requires.
  </Card>
</CardGroup>


## Related topics

- [Package Version Compatibility Management](/en/advanced/package-versioning.md)
- [Queues](/en/queues.md)
- [Laravel Sentinel — route protection middleware research](/en/blog/sentinel-introduction.md)
- [April 2026 Laravel updates](/en/blog/changelog/202604.md)
- [May 2026 Laravel updates](/en/blog/changelog/202605.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.