Laravel Horizon: Metrics, Supervisor Tuning &amp; Safe Deploys | 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 Safe Deployments        On this page       1. [  Laravel Horizon in Production: Beyond the Pretty Dashboard ](#laravel-horizon-in-production-beyond-the-pretty-dashboard)
2. [  Querying the Metrics API ](#querying-the-metrics-api)
3. [  Supervisor Configuration for Mixed Workloads ](#supervisor-configuration-for-mixed-workloads)
4. [  Balance Strategies ](#balance-strategies)
5. [  Safe Deployments Without Dropping Jobs ](#safe-deployments-without-dropping-jobs)
6. [  Systemd Unit Example ](#systemd-unit-example)
7. [  Horizon in Docker / Kubernetes ](#horizon-in-docker-kubernetes)
8. [  Takeaways ](#takeaways)

  ![Laravel Horizon: Queue Metrics, Supervisor Tuning, and Safe Deployments](https://cdn.msaied.com/660/4e7fb1097d66f5b5c6bb68e5aad9b211.png)

  #laravel   #horizon   #queues   #devops  

 Laravel Horizon: Queue Metrics, Supervisor Tuning, and Safe Deployments 
=========================================================================

     12 Sep 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  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   Querying the Metrics API  ](#querying-the-metrics-api)
3. [  03   Supervisor Configuration for Mixed Workloads  ](#supervisor-configuration-for-mixed-workloads)
4. [  04   Balance Strategies  ](#balance-strategies)
5. [  05   Safe Deployments Without Dropping Jobs  ](#safe-deployments-without-dropping-jobs)
6. [  06   Systemd Unit Example  ](#systemd-unit-example)
7. [  07   Horizon in Docker / Kubernetes  ](#horizon-in-docker-kubernetes)
8. [  08   Takeaways  ](#takeaways)

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

Horizon ships with a slick UI, but the real value for a senior engineer is in its programmatic metrics API, fine-grained supervisor configuration, and the ability to drain workers cleanly during deployments. This article focuses on those three areas with concrete examples.

---

Querying the Metrics API
------------------------

Horizon stores throughput and runtime snapshots in Redis. You can read them directly via the `Horizon\Contracts\MetricsRepository` binding rather than scraping the UI.

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

$metrics = app(MetricsRepository::class);

// Throughput (jobs/min) for the last recorded snapshot
$throughput = $metrics->throughput();

// Runtime in milliseconds for a specific job class
$runtime = $metrics->runtimeForJob(ProcessInvoice::class);

// All measured queues
$queues = $metrics->measuredQueues();

```

This is useful for custom alerting: pipe `$throughput` into a Prometheus pushgateway or a Slack webhook when it drops below a threshold, without depending on a third-party APM.

---

Supervisor Configuration for Mixed Workloads
--------------------------------------------

A single supervisor with a high `maxProcesses` count is the most common Horizon misconfiguration. Different job classes have wildly different runtimes and resource profiles. Separate them.

```php
// config/horizon.php
'environments' => [
    'production' => [
        'supervisor-critical' => [
            'connection' => 'redis',
            'queue'      => ['critical'],
            'balance'    => 'auto',
            'minProcesses' => 2,
            'maxProcesses' => 10,
            'tries'      => 3,
            'timeout'    => 30,
        ],
        'supervisor-default' => [
            'connection' => 'redis',
            'queue'      => ['default'],
            'balance'    => 'simple',
            'processes'  => 4,
            'tries'      => 3,
            'timeout'    => 90,
        ],
        'supervisor-heavy' => [
            'connection' => 'redis',
            'queue'      => ['exports', 'reports'],
            'balance'    => 'simple',
            'processes'  => 2,
            'tries'      => 1,
            'timeout'    => 600,
        ],
    ],
],

```

### Balance Strategies

- **`simple`** — distributes processes evenly across queues in the list. Good when queue depths are predictable.
- **`auto`** — scales processes toward queues with the longest wait time. Use this for `critical` where latency matters.
- **`false`** — no balancing; each process works the queues in order. Rarely correct in production.

Set `balanceCooldown` (seconds) when using `auto` to prevent thrashing:

```php
'balanceCooldown' => 3,
'minProcesses'    => 1,
'maxProcesses'    => 15,

```

---

Safe Deployments Without Dropping Jobs
--------------------------------------

The naive `php artisan horizon:terminate` in a deploy script kills workers immediately, potentially mid-job. The correct sequence:

```bash
# 1. Tell Horizon to finish current jobs then exit
php artisan horizon:pause

# 2. Run your deployment steps (composer, migrations, asset build)
# ...

# 3. Restart Horizon — supervisor (systemd/Forge) brings it back
php artisan horizon:terminate

```

`horizon:pause` stops workers from picking up new jobs but lets in-flight jobs complete. `horizon:terminate` then sends `SIGTERM` to the master process, which waits for workers to finish before exiting.

### Systemd Unit Example

```ini
[Unit]
Description=Laravel Horizon
After=network.target

[Service]
User=forge
ExecStart=/usr/bin/php /var/www/app/artisan horizon
Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=3600

[Install]
WantedBy=multi-user.target

```

Setting `TimeoutStopSec=3600` gives long-running export jobs up to an hour to finish before systemd force-kills the process. Tune this to your longest expected job runtime.

### Horizon in Docker / Kubernetes

In a container environment, trap `SIGTERM` and delegate to Horizon's own signal handler:

```dockerfile
CMD ["php", "artisan", "horizon"]

```

Kubernetes sends `SIGTERM` on pod termination. Horizon's master process catches it and begins a graceful shutdown. Set `terminationGracePeriodSeconds` in your pod spec to match `TimeoutStopSec`.

---

Takeaways
---------

- Use `MetricsRepository` directly for custom alerting instead of polling the UI.
- Separate supervisors by job profile (latency-sensitive, long-running, bulk) rather than one fat supervisor.
- Use `balance => auto` with a cooldown for variable-load queues; use `simple` for predictable ones.
- Always `horizon:pause` before deploying, then `horizon:terminate` after assets are in place.
- Match `TimeoutStopSec` / `terminationGracePeriodSeconds` to your longest job timeout to avoid mid-job kills.

 Found this useful?

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

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

  3 questions  

     Q01  What is the difference between `horizon:terminate` and `horizon:pause`?        `horizon:pause` stops workers from pulling new jobs but lets current jobs finish. `horizon:terminate` sends SIGTERM to the Horizon master process, which then waits for all workers to complete before exiting. Use pause first during deploys, then terminate after your code is in place. 

      Q02  When should I use `balance =&gt; auto` vs `balance =&gt; simple`?        Use `auto` when queue depths fluctuate and you want Horizon to shift workers toward the busiest queue automatically. Use `simple` when queue depths are predictable and you want an even split. Always set `balanceCooldown` with `auto` to prevent rapid process thrashing. 

      Q03  How do I expose Horizon metrics to an external monitoring system?        Inject `Laravel\Horizon\Contracts\MetricsRepository` and call `throughput()` or `runtimeForJob()` inside a scheduled command. From there you can push values to Prometheus, Datadog, or any custom webhook without relying on the Horizon UI or a third-party package. 

  Continue reading

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

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

 [ ![Filament v4 Table Bulk Actions: Custom Confirmation Modals and Scoped Authorization](https://cdn.msaied.com/661/5f319b485f1bc0c76e2c82746f730c8c.png) filament laravel authorization 

### Filament v4 Table Bulk Actions: Custom Confirmation Modals and Scoped Authorization

Go beyond the default delete bulk action. Learn how to build custom Filament v4 bulk actions with typed confir...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 12 Sep 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/filament-v4-table-bulk-actions-custom-confirmation-modals-and-scoped-authorization) [ ![Custom Eloquent Casts: Value Objects, Enums, and Encrypted Composites](https://cdn.msaied.com/659/44f701e1dc43e64d0b7ecc984d0b34bc.png) laravel eloquent ddd 

### Custom Eloquent Casts: Value Objects, Enums, and Encrypted Composites

Go beyond primitive storage with Eloquent's CastsAttributes contract. Build reusable value-object casts, handl...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 12 Sep 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/custom-eloquent-casts-value-objects-enums-and-encrypted-composites) [ ![Laravel Macro-Free Extensibility: Custom Query Builder Classes and Fluent Scopes](https://cdn.msaied.com/658/b45c3cc06b92a332e526bed9bb1f826d.png) laravel eloquent architecture 

### Laravel Macro-Free Extensibility: Custom Query Builder Classes and Fluent Scopes

Skip global macros and reach for typed, testable custom query builder classes in Laravel. Learn how to bind a...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 11 Sep 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-macro-free-extensibility-custom-query-builder-classes-and-fluent-scopes) 

   [  ![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)
