Laravel Horizon Tuning &amp; Production Scaling | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Horizon: Queue Metrics, Supervisor Tuning, and Graceful Scaling in Production        On this page       1. [  Laravel Horizon in Production: Beyond the Pretty Dashboard ](#laravel-horizon-in-production-beyond-the-pretty-dashboard)
2. [  How Horizon Supervisors Actually Work ](#how-horizon-supervisors-actually-work)
3. [  Choosing the Right Balance Strategy ](#choosing-the-right-balance-strategy)
4. [  Reading Horizon Metrics Programmatically ](#reading-horizon-metrics-programmatically)
5. [  Graceful Termination and Long-Running Jobs ](#graceful-termination-and-long-running-jobs)
6. [  Memory Leaks and maxJobs ](#memory-leaks-and-codemaxjobscode)
7. [  Takeaways ](#takeaways)

  ![Laravel Horizon: Queue Metrics, Supervisor Tuning, and Graceful Scaling in Production](https://cdn.msaied.com/390/25668fca0fa0cd1acbd22f27230b8ef5.png)

  #laravel   #horizon   #queues   #production   #redis  

 Laravel Horizon: Queue Metrics, Supervisor Tuning, and Graceful Scaling in Production 
=======================================================================================

     8 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Laravel Horizon in Production: Beyond the Pretty Dashboard  ](#laravel-horizon-in-production-beyond-the-pretty-dashboard)
2. [  02   How Horizon Supervisors Actually Work  ](#how-horizon-supervisors-actually-work)
3. [  03   Choosing the Right Balance Strategy  ](#choosing-the-right-balance-strategy)
4. [  04   Reading Horizon Metrics Programmatically  ](#reading-horizon-metrics-programmatically)
5. [  05   Graceful Termination and Long-Running Jobs  ](#graceful-termination-and-long-running-jobs)
6. [  06   Memory Leaks and maxJobs  ](#memory-leaks-and-codemaxjobscode)
7. [  07   Takeaways  ](#takeaways)

 Laravel Horizon in Production: Beyond the Pretty Dashboard
----------------------------------------------------------

Horizon ships with a slick UI, but most teams stop there. The real value is in its supervisor model, metric snapshots, and the knobs that determine whether your queue survives a traffic spike or silently drops work.

### How Horizon Supervisors Actually Work

Horizon manages one or more *supervisor* processes, each of which spawns a pool of `queue:work` child processes. The key insight: **supervisors are not queues**. A single supervisor can watch multiple queues with a priority order.

```php
// config/horizon.php
'environments' => [
    'production' => [
        'supervisor-default' => [
            'connection' => 'redis',
            'queue' => ['critical', 'default', 'low'],
            'balance' => 'auto',
            'autoScalingStrategy' => 'time',
            'maxProcesses' => 20,
            'minProcesses' => 2,
            'maxTime' => 0,
            'maxJobs' => 500,
            'memory' => 256,
            'tries' => 3,
            'timeout' => 90,
            'nice' => 0,
        ],
    ],
],

```

`balance => 'auto'` tells Horizon to redistribute workers across queues based on wait time. `autoScalingStrategy => 'time'` scales toward minimising wait time rather than raw queue size — better for latency-sensitive work.

### Choosing the Right Balance Strategy

| Strategy | Scales on | Best for | |---|---|---| | `simple` | Queue size ratio | Predictable, uniform jobs | | `auto` | Wait time | Mixed job durations | | `false` | Fixed processes | Isolated, critical queues |

For a SaaS with a `critical` queue (password resets, webhooks) and a `low` queue (report generation), use **two supervisors** with `balance => false` on critical and `auto` on low:

```php
'supervisor-critical' => [
    'queue' => ['critical'],
    'balance' => false,
    'minProcesses' => 3,
    'maxProcesses' => 3,
],
'supervisor-bulk' => [
    'queue' => ['default', 'low'],
    'balance' => 'auto',
    'minProcesses' => 1,
    'maxProcesses' => 15,
    'autoScalingStrategy' => 'time',
],

```

This guarantees critical work always has dedicated workers, while bulk work scales elastically.

### Reading Horizon Metrics Programmatically

Horizon stores throughput and runtime snapshots in Redis. You can query them directly:

```php
use Laravel\Horizon\Contracts\MetricsRepository;

$metrics = app(MetricsRepository::class);

$throughput = $metrics->throughputForQueue('default'); // jobs/min
$runtime = $metrics->runtimeForQueue('default');       // ms average

```

Pipe these into your observability stack (Datadog, Prometheus via a custom exporter) rather than relying solely on the Horizon UI. Alert when `throughputForQueue` drops to zero during business hours — that's a dead worker, not an empty queue.

### Graceful Termination and Long-Running Jobs

Horizon sends `SIGTERM` to workers during deploys. Workers finish their current job, then exit. The danger: a job that runs for 180 seconds will block the deploy for 180 seconds per worker.

Mitigate with `maxTime` (seconds a worker runs before self-terminating after its current job) and by designing long jobs to be interruptible:

```php
public function handle(): void
{
    foreach ($this->chunks() as $chunk) {
        if ($this->job->isDeleted() || $this->job->isReleased()) {
            return;
        }

        $this->processChunk($chunk);

        // Respect the terminate signal between chunks
        if (app('queue.worker')->shouldQuit) {
            $this->release(5);
            return;
        }
    }
}

```

Accessing `app('queue.worker')->shouldQuit` lets a job voluntarily release itself back to the queue when a shutdown is pending, preventing message loss.

### Memory Leaks and `maxJobs`

PHP workers accumulate memory over time — Eloquent model event listeners, static caches, and package bootstrapping all contribute. `maxJobs => 500` forces a worker restart after 500 jobs, which is a pragmatic ceiling. Pair it with `memory => 256` (MB) as a hard cap:

```php
'maxJobs' => 500,
'memory' => 256,

```

Monitor actual worker memory with `horizon:snapshot` on a schedule:

```php
// app/Console/Kernel.php
$schedule->command('horizon:snapshot')->everyFiveMinutes();

```

Without snapshots, the Horizon metrics graphs are empty.

### Takeaways

- Use **two supervisors** to isolate critical queues from bulk work; never mix them with `auto` balance.
- Set `autoScalingStrategy => 'time'` for latency-sensitive queues; `size` can over-provision on bursty workloads.
- Export Horizon metrics to your APM — don't rely on the UI for alerting.
- Design long jobs to check `shouldQuit` between chunks so deploys don't stall.
- Schedule `horizon:snapshot` every five minutes or your throughput graphs will be blank.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-horizon-queue-metrics-supervisor-tuning-and-graceful-scaling-in-production&text=Laravel+Horizon%3A+Queue+Metrics%2C+Supervisor+Tuning%2C+and+Graceful+Scaling+in+Production) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-horizon-queue-metrics-supervisor-tuning-and-graceful-scaling-in-production) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  What is the difference between `balance =&gt; 'auto'` and `balance =&gt; 'simple'` in Horizon?        `simple` distributes workers proportionally to queue size. `auto` targets wait time, so it shifts workers toward queues where jobs are waiting longest regardless of raw count — better when jobs have variable durations. 

      Q02  How do I prevent a long-running job from blocking a Horizon deploy?        Check `app('queue.worker')-&gt;shouldQuit` between processing units inside your job. When it is true, call `$this-&gt;release()` to put the job back on the queue and return early, allowing the worker to exit cleanly. 

      Q03  Why are my Horizon throughput graphs empty?        Horizon only records metric snapshots when `horizon:snapshot` runs. Schedule it every five minutes in your console kernel; without it, the metrics repository has no data to display. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![HTTP Query Method Support in Laravel 13.19](https://cdn.msaied.com/394/4c917aad559beddb5eeb9a7a1e107f4c.png) Laravel Laravel 13 HTTP Client 

### HTTP Query Method Support in Laravel 13.19

Laravel 13.19 adds Http::query() for the HTTP QUERY verb, query/queryJson testing helpers, a reduceInto() coll...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 8 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/http-query-method-support-in-laravel-1319) [ ![Blackfire & Xdebug Profiling in Laravel: Finding Real Bottlenecks in Production-Like Environments](https://cdn.msaied.com/393/a8dfc4ec52c4febc3ef41a491eb452ea.png) laravel performance profiling 

### Blackfire &amp; Xdebug Profiling in Laravel: Finding Real Bottlenecks in Production-Like Environments

Stop guessing where your Laravel app is slow. This guide walks through practical Blackfire and Xdebug profilin...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 8 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/blackfire-xdebug-profiling-in-laravel-finding-real-bottlenecks-in-production-like-environments) [ ![Laravel Cloud Security Defaults Behind Every Deploy](https://cdn.msaied.com/395/28df8f542fbe81ecee3ba4ad897f36d6.png) Laravel Cloud Security PHP 

### Laravel Cloud Security Defaults Behind Every Deploy

Laravel Cloud ships WAF rules, DDoS mitigation, automatic PHP patching, SOC 2 Type II compliance, and deploy-t...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 8 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-cloud-security-defaults-behind-every-deploy) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
