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

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

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

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Laravel Horizon: Queue Metrics, Supervisor Tuning, and Reliable Job Throughput        On this page       1. [  Laravel Horizon: Beyond the Default Config ](#laravel-horizon-beyond-the-default-config)
2. [  Supervisor Configuration That Actually Reflects Priority ](#supervisor-configuration-that-actually-reflects-priority)
3. [  Reading Horizon Metrics Programmatically ](#reading-horizon-metrics-programmatically)
4. [  Jobs That Survive Worker Restarts ](#jobs-that-survive-worker-restarts)
5. [  The Pattern: Idempotency Key + Atomic Check ](#the-pattern-idempotency-key-atomic-check)
6. [  Takeaways ](#takeaways)

  ![Laravel Horizon: Queue Metrics, Supervisor Tuning, and Reliable Job Throughput](https://cdn.msaied.com/313/b64554c2fce0b1ec694c929b7bff1e38.png)

  #laravel   #horizon   #queues   #performance  

 Laravel Horizon: Queue Metrics, Supervisor Tuning, and Reliable Job Throughput 
================================================================================

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

       Table of contents

1. [  01   Laravel Horizon: Beyond the Default Config  ](#laravel-horizon-beyond-the-default-config)
2. [  02   Supervisor Configuration That Actually Reflects Priority  ](#supervisor-configuration-that-actually-reflects-priority)
3. [  03   Reading Horizon Metrics Programmatically  ](#reading-horizon-metrics-programmatically)
4. [  04   Jobs That Survive Worker Restarts  ](#jobs-that-survive-worker-restarts)
5. [  05   The Pattern: Idempotency Key + Atomic Check  ](#the-pattern-idempotency-key-atomic-check)
6. [  06   Takeaways  ](#takeaways)

 Laravel Horizon: Beyond the Default Config
------------------------------------------

Most teams install Horizon, paste the default `config/horizon.php`, and call it done. That works until traffic spikes, a slow third-party API blocks your `default` queue, and your `critical` payment jobs sit waiting behind a backlog of email sends.

This article covers three concrete improvements: priority-aware supervisor configuration, reading Horizon's metrics API to drive alerting, and writing jobs that survive unexpected worker termination.

---

Supervisor Configuration That Actually Reflects Priority
--------------------------------------------------------

Horizon's `supervisors` key maps directly to separate worker pools. The key insight is that each supervisor is an independent process group — they do not share a queue slot budget.

```php
// config/horizon.php
'environments' => [
    'production' => [
        'supervisor-critical' => [
            'connection' => 'redis',
            'queue' => ['critical'],
            'balance' => 'simple',
            'minProcesses' => 3,
            'maxProcesses' => 10,
            'tries' => 3,
            'timeout' => 30,
        ],
        'supervisor-default' => [
            'connection' => 'redis',
            'queue' => ['default', 'notifications'],
            'balance' => 'auto',
            'minProcesses' => 2,
            'maxProcesses' => 20,
            'balanceCooldown' => 3,
            'tries' => 5,
            'timeout' => 90,
        ],
    ],
],

```

`balance => auto` lets Horizon scale workers proportionally to queue depth. Use `simple` for critical queues where you want a fixed floor of workers always ready, not waiting for the autoscaler to react.

`balanceCooldown` (seconds) prevents thrashing — Horizon won't add or remove workers more often than this interval. Set it higher (5–10) on queues with bursty but short-lived spikes.

---

Reading Horizon Metrics Programmatically
----------------------------------------

Horizon stores throughput and runtime metrics in Redis. You can read them via the `Metrics` repository to drive custom dashboards or Slack alerts.

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

$metrics = app(MetricsRepository::class);

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

if ($throughput < 10 && $runtime > 5000) {
    // Alert: critical queue is slow and under-processed
    Log::channel('slack')->critical('Horizon critical queue degraded', [
        'throughput' => $throughput,
        'avg_runtime_ms' => $runtime,
    ]);
}

```

Wrap this in a scheduled command running every minute:

```php
// routes/console.php
Schedule::command('horizon:metrics:check')->everyMinute()->withoutOverlapping();

```

This gives you alerting without a paid APM tool for queue-specific health.

---

Jobs That Survive Worker Restarts
---------------------------------

When you run `horizon:terminate` or deploy a new release, Horizon sends `SIGTERM` to workers. A job mid-execution gets re-queued if it hasn't been deleted — but only if you haven't already committed a side effect.

### The Pattern: Idempotency Key + Atomic Check

```php
class ChargeSubscription implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public string $idempotencyKey;

    public function __construct(public int $subscriptionId)
    {
        $this->idempotencyKey = 'charge:' . $subscriptionId . ':' . now()->format('Y-m-d');
    }

    public function handle(PaymentGateway $gateway): void
    {
        $alreadyCharged = Cache::add(
            $this->idempotencyKey,
            true,
            now()->addDay()
        );

        if (! $alreadyCharged) {
            return; // Re-queued after SIGTERM — skip silently
        }

        $gateway->charge($this->subscriptionId);
    }
}

```

`Cache::add` is atomic on Redis — it only sets the key if it does not exist, returning `false` if the key was already present. This prevents double-charges when a job is re-queued after an interrupted worker.

---

Takeaways
---------

- **Separate supervisors per priority** — never let low-priority jobs compete for the same worker pool as critical ones.
- **Use `balance => simple` with a `minProcesses` floor** for queues where latency matters more than cost.
- **`MetricsRepository` is public API** — use it for custom alerting before reaching for an APM.
- **`Cache::add` is your idempotency primitive** — combine it with a deterministic key to make re-queued jobs safe.
- **`balanceCooldown` prevents autoscaler thrashing** — tune it per queue's burst profile, not globally.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-horizon-queue-metrics-supervisor-tuning-and-reliable-job-throughput-1&text=Laravel+Horizon%3A+Queue+Metrics%2C+Supervisor+Tuning%2C+and+Reliable+Job+Throughput) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-horizon-queue-metrics-supervisor-tuning-and-reliable-job-throughput-1) 

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

  3 questions  

     Q01  How does Horizon's `balance =&gt; auto` differ from `balance =&gt; simple`?        `auto` scales worker count proportionally to queue depth across all queues in that supervisor, redistributing processes dynamically. `simple` keeps a fixed number of workers per queue without redistribution. Use `simple` when you need a guaranteed minimum for latency-sensitive queues. 

      Q02  Will a job be lost if a Horizon worker receives SIGTERM mid-execution?        No — Horizon re-queues the job if it has not been deleted from Redis yet. The risk is duplicate execution, not data loss. Guard against duplicates with an idempotency key stored atomically in Redis via `Cache::add`. 

      Q03  Can I use `MetricsRepository` outside of Horizon's dashboard?        Yes. It is bound in the service container and can be resolved anywhere in your application. Horizon must be running and processing jobs for the metrics to be populated, but reading them is safe from any context including scheduled commands. 

  Continue reading

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

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

 [ ![Filament v5 Preview: Schema Unification, Performance Shifts, and How to Prepare](https://cdn.msaied.com/340/1a05ca68637b898b676efb66f22e627f.png) filament laravel php 

### Filament v5 Preview: Schema Unification, Performance Shifts, and How to Prepare

Filament v5 is reshaping how panels, forms, and tables are composed. This deep-dive covers the confirmed archi...

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

 1 Jul 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/filament-v5-preview-schema-unification-performance-shifts-and-how-to-prepare) [ ![Laravel 13: New Features, Helpers, and Practical Upgrade Notes](https://cdn.msaied.com/339/58c4fa6fe9b6d25a2dac17c621b6f4c6.png) laravel laravel-13 upgrade 

### Laravel 13: New Features, Helpers, and Practical Upgrade Notes

Laravel 13 ships with async-first defaults, a leaner bootstrapping layer, and several quality-of-life helpers....

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

 1 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-13-new-features-helpers-and-practical-upgrade-notes) [ ![Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping](https://cdn.msaied.com/337/05b39d16d0f88a5fb94d0cf74049b88b.png) laravel laravel-12 upgrade 

### Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping

Laravel 12 ships with a leaner skeleton, first-class route file organisation, and a revised application bootst...

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

 1 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-12-structured-route-files-slim-skeletons-and-the-new-application-bootstrapping) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.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://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.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)
