Laravel Horizon Tuning &amp; Reliable Job Throughput | 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 Queues: Horizon Metrics, Supervisor Tuning, and Reliable Job Throughput        On this page       1. [  Beyond php artisan queue:work: Running Queues Seriously in Production ](#beyond-codephp-artisan-queueworkcode-running-queues-seriously-in-production)
2. [  Horizon Balancing Strategies ](#horizon-balancing-strategies)
3. [  Supervisor: Zero-Downtime Deploys ](#supervisor-zero-downtime-deploys)
4. [  Job Timeouts and the SIGKILL Problem ](#job-timeouts-and-the-codesigkillcode-problem)
5. [  Observability: Metrics That Matter ](#observability-metrics-that-matter)
6. [  Practical Takeaways ](#practical-takeaways)

  ![Laravel Queues: Horizon Metrics, Supervisor Tuning, and Reliable Job Throughput](https://cdn.msaied.com/329/b8b8a1253f218f9cae9c423ea28ec877.png)

  #laravel   #queues   #horizon   #devops   #performance  

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

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

       Table of contents

1. [  01   Beyond php artisan queue:work: Running Queues Seriously in Production  ](#beyond-codephp-artisan-queueworkcode-running-queues-seriously-in-production)
2. [  02   Horizon Balancing Strategies  ](#horizon-balancing-strategies)
3. [  03   Supervisor: Zero-Downtime Deploys  ](#supervisor-zero-downtime-deploys)
4. [  04   Job Timeouts and the SIGKILL Problem  ](#job-timeouts-and-the-codesigkillcode-problem)
5. [  05   Observability: Metrics That Matter  ](#observability-metrics-that-matter)
6. [  06   Practical Takeaways  ](#practical-takeaways)

 Beyond `php artisan queue:work`: Running Queues Seriously in Production
-----------------------------------------------------------------------

Most Laravel developers know how to push a job and run a worker. Far fewer have thought carefully about *how many* workers to run, *which* balancing strategy to use, or *what happens* when a deploy kills a long-running job mid-execution. This article covers the practical decisions that separate a fragile queue setup from a production-grade one.

---

Horizon Balancing Strategies
----------------------------

Horizon ships with three balancing strategies: `simple`, `auto`, and `false` (manual). The choice has real consequences.

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

```

**`auto`** measures throughput per queue and redistributes workers every `balanceCooldown` seconds, shifting at most `balanceMaxShift` processes at a time. This is the right default for mixed-priority workloads.

**`simple`** divides workers evenly across queues — predictable but blind to actual queue depth.

Keep `balanceMaxShift` low (2–3) to avoid thrashing. A sudden spike that shifts 10 workers at once starves other queues for the cooldown window.

---

Supervisor: Zero-Downtime Deploys
---------------------------------

Horizon itself is managed by Supervisor. The critical piece most teams miss is graceful termination on deploy.

```ini
; /etc/supervisor/conf.d/horizon.conf
[program:horizon]
process_name=%(program_name)s
command=php /var/www/artisan horizon
autostart=true
autorestart=true
stopwaitsecs=3600
stopsignal=SIGTERM
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/horizon.log

```

`stopwaitsecs=3600` tells Supervisor to wait up to one hour for Horizon to finish in-flight jobs before force-killing. Without this, a deploy during a long job causes a silent partial execution.

In your deploy script:

```bash
php artisan horizon:terminate
sudo supervisorctl restart horizon

```

`horizon:terminate` signals Horizon to stop accepting new jobs and wait for current workers to finish. Supervisor then restarts the process cleanly with the new code.

---

Job Timeouts and the `SIGKILL` Problem
--------------------------------------

PHP's `pcntl` extension allows Horizon to send `SIGTERM` to a worker when its `timeout` is exceeded. The worker can catch this and clean up. But if `timeout` in `horizon.php` is *shorter* than the job's own `$timeout` property, the worker is killed before the job can handle it.

```php
class GenerateReport implements ShouldQueue
{
    public int $timeout = 120; // job-level
    public int $tries = 2;

    public function handle(): void
    {
        // long-running work
    }

    public function failed(Throwable $e): void
    {
        // cleanup, notify, etc.
    }
}

```

Rule: the Horizon supervisor `timeout` should always be slightly *higher* than the longest job `$timeout` in that queue. A 10-second buffer is sufficient.

---

Observability: Metrics That Matter
----------------------------------

Horizon exposes a `/horizon/api/stats` endpoint and stores metrics in Redis. Expose these to your monitoring stack:

```php
// In a scheduled command or Prometheus exporter
$stats = app(\Laravel\Horizon\Contracts\MetricsRepository::class);

$throughput = $stats->throughput(); // jobs/min across all queues
$runtime = $stats->runtimeForQueue('default'); // avg ms
$failRate = $stats->failedJobsPerMinute();

```

Alert on:

- Queue depth growing faster than throughput (workers undersized)
- Average runtime increasing over time (memory leak or slow dependency)
- Failed jobs per minute spiking (upstream service degradation)

---

Practical Takeaways
-------------------

- Use `auto` balancing with conservative `balanceMaxShift` and `balanceCooldown` values.
- Set `stopwaitsecs` in Supervisor to at least your longest expected job runtime.
- Always call `horizon:terminate` before restarting — never `supervisorctl restart` cold.
- Keep Horizon supervisor `timeout` slightly above the job-level `$timeout`.
- Instrument throughput, runtime, and failure rate; queue depth alone is not enough.
- Separate high-priority work into a dedicated `critical` queue with its own supervisor block.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-queues-horizon-metrics-supervisor-tuning-and-reliable-job-throughput&text=Laravel+Queues%3A+Horizon+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-queues-horizon-metrics-supervisor-tuning-and-reliable-job-throughput) 

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

  3 questions  

     Q01  What is the difference between Horizon's `auto` and `simple` balancing strategies?        `auto` dynamically redistributes workers based on measured queue throughput, shifting processes toward busier queues within the configured cooldown window. `simple` divides workers evenly across all queues regardless of actual depth, which is predictable but inefficient under uneven load. 

      Q02  Why does `stopwaitsecs` in Supervisor matter for Laravel Horizon?        Without a sufficient `stopwaitsecs`, Supervisor force-kills the Horizon process before in-flight jobs finish when you restart during a deploy. Setting it to at least your longest expected job duration gives `horizon:terminate` time to drain workers gracefully. 

      Q03  How do I prevent jobs from being silently killed mid-execution on deploy?        Run `php artisan horizon:terminate` in your deploy script before restarting Supervisor. This signals Horizon to stop accepting new jobs and wait for current workers to complete, then Supervisor restarts the process with fresh code. 

  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)
