Laravel Horizon in production — sizing workers, surviving Redis, and the retry strategy
May 6, 2026 · 1 min read · by Sudhanshu K.
Horizon makes Laravel queues legible. The dashboard is great, the supervisor model is genuinely useful, and the failover story is correct. What it doesn't do is decide your worker sizing, queue isolation strategy, or retry policy — those are still your problems, and the defaults are conservative in ways that hide real production issues.
This is the Horizon configuration we ship on every managed Laravel install that does meaningful background work.
A production supervisor config
'environments' => [
'production' => [
'supervisor-default' => [
'connection' => 'redis',
'queue' => ['critical', 'default'],
'balance' => 'auto',
'minProcesses' => 4,
'maxProcesses' => 20,
'balanceMaxShift' => 2,
'balanceCooldown' => 3,
'tries' => 5,
'timeout' => 60,
],
'supervisor-long' => [
'connection' => 'redis',
'queue' => ['long-running'],
'balance' => 'simple',
'maxProcesses' => 4,
'timeout' => 600,
],
],
],Two supervisors: one for short, high-priority jobs (balanced auto, more workers), one for long-running batch jobs (fixed workers, longer timeout, no balancing). Putting both on the same supervisor with the same timeout is the single most common Horizon misconfiguration we see.
The full write-up covers:
- Queue isolation — why "default" should not be the only queue
balance: autovsbalance: simplevsbalance: false- Redis persistence (AOF every-second) and what happens when Redis restarts
- The retry-backoff helper and idempotency on inbound webhook handlers
- Memory leak detection —
--memory=128and the worker recycle pattern - Failed jobs dashboard hygiene and the alerts we wire on the failed_jobs table
We ship this Horizon configuration on every managed Laravel deployment.
Full article available
Read the full article