
August 17, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
If you are running background jobs in a production PHP application, you need visibility into what is actually processing, failing, or stuck. Laravel Horizon: Monitor and Scale Your Queues provides the dashboard and configuration layer necessary to manage Redis-backed workers without guessing at process counts or retry thresholds. For developers building high-traffic systems, understanding this tool is as critical as knowing Laravel API best practices for request handling. This guide covers the exact configuration, infrastructure requirements, and scaling strategies I use on client projects to keep job throughput stable under load.
Why Use Laravel Horizon to Monitor and Scale Your Queues?
Standard Laravel queue workers run blindly. You can start ten workers via Supervisor, but you have no native insight into whether three are idle while seven are choking on a slow export job. Horizon solves this opacity by sitting between your application and Redis, tracking metrics like throughput, wait times, and failure rates in real time.
In my experience working on production Laravel applications, particularly legal-tech portals and eCommerce platforms, the moment you move beyond simple email queues to complex document generation or payment reconciliation, static worker counts fail. You either over-provision servers (wasting money) or under-provision them (causing backlogs). Horizon allows you to define scaling logic in code rather than server config.
Unlike basic queue monitoring tools, Horizon persists metrics in Redis itself, meaning your dashboard survives application restarts and reflects historical trends. When you need to explain to a stakeholder why invoice generation slowed down during peak hours, Horizon’s charts provide the evidence. For teams managing multiple environments, having environment-specific scaling configurations committed to version control eliminates the "it works on staging" problem entirely.
How Do You Install and Configure Laravel Horizon for Production?
Installation is straightforward, but production configuration requires attention to detail. Horizon requires Redis as your queue driver; it does not support database, SQS, or Beanstalkd drivers for its monitoring features. Ensure your QUEUE_CONNECTION is set to redis in your .env file before proceeding.
Installation Steps
- Install via Composer:
composer require laravel/horizon - Publish assets and configuration:
php artisan horizon:install - Configure authorization in
app/Providers/HorizonServiceProvider.php. Never leave the gate open in production. A typical check verifies admin status or specific permissions. - Run migrations if you intend to store long-term metrics (optional but recommended for audit trails):
php artisan migrate
Environment-Specific Configuration
The published config/horizon.php file contains an environments array. This is where you define how many workers run in production versus local development. On a real client project, I typically set conservative defaults and tune upward based on observed throughput.
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default', 'high', 'low'],
'balance' => 'auto',
'maxProcesses' => 20,
'memory' => 256,
'tries' => 3,
'timeout' => 300,
'nice' => 0,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
], Note the memory limit. Set this below your PHP CLI memory limit to prevent OOM kills. If your jobs process large files or datasets, profile them first. I’ve seen document generation jobs in legal-tech portals consume 400MB+; setting Horizon’s limit to 256MB caused silent failures until we adjusted both PHP and Horizon configs.
What Balancing Strategy Should You Use for Queue Workers?
Horizon offers three balancing modes: simple, false, and auto. Choosing correctly determines whether your system adapts to load or wastes resources.
| Strategy | Behavior | Best For | Trade-offs |
|---|---|---|---|
simple | Fixed number of workers per queue | Predictable, uniform workloads | No adaptation to spikes; manual tuning required |
false | Disables Horizon worker management | Using external orchestrators (Kubernetes) | Loses auto-scaling; must manage processes externally |
auto | Dynamically adjusts workers based on queue length | Variable traffic, mixed job priorities | Slightly higher Redis overhead; requires tuning min/max |
For most production Laravel applications, auto is the correct default. It scales workers up when queues back up and down when they clear, within the bounds of minProcesses and maxProcesses. The key parameter often overlooked is balanceCooldown, which prevents rapid flapping when queue lengths oscillate. Set this to 3–5 seconds in production to stabilize worker counts.
When configuring auto mode, set minProcesses to handle your baseline load and maxProcesses to match your server’s capacity. A common mistake is setting maxProcesses too high relative to available RAM. Each worker consumes memory; twenty workers at 256MB each requires 5GB just for PHP processes, excluding Redis and Nginx. Always calculate total memory headroom before increasing concurrency.
How Do You Handle Failed Jobs and Retries Safely?
Horizon’s failed job dashboard is invaluable, but safe retry handling requires configuration beyond the UI. Define tries and timeout per supervisor in your config, not globally. Different job types have different tolerance for retries and execution time.
For payment webhook processing on eCommerce sites, I typically set tries to 5 with exponential backoff. For PDF generation in legal-tech portals, tries might be 2 with a longer timeout. Configure this by defining multiple supervisors targeting specific queues:
'supervisor-payments' => [
'queue' => ['payments'],
'tries' => 5,
'backoff' => [60, 300, 900, 3600, 7200],
'timeout' => 120,
],
'supervisor-documents' => [
'queue' => ['documents'],
'tries' => 2,
'timeout' => 600,
], Always implement the failed() method on jobs that trigger side effects. If a payment notification fails after all retries, log it to a dedicated channel or notify an admin. Silent failures in financial workflows are unacceptable. Horizon captures the exception, but business logic for escalation belongs in the job class itself.
What Infrastructure Does Laravel Horizon Require in Production?
Horizon adds operational requirements beyond standard Laravel. Understanding these prevents deployment surprises, especially when working with teams familiar with Laravel development in Nepal where shared hosting constraints sometimes influence architecture decisions.
- Redis is mandatory. Horizon stores all metrics, tags, and job state in Redis. Use Redis 7.x or later; older versions lack some stream commands Horizon relies on. Dedicated Redis instances are preferred over shared caches to avoid metric loss during cache flushes.
- Supervisor or systemd is still required. Horizon manages worker processes, but something must manage the Horizon master process itself. Use Supervisor or systemd to ensure
horizon:workrestarts on failure. Never run it manually in production. - PHP extensions. Ensure
pcntl,posix, andredisextensions are installed and enabled. Missingpcntlcauses silent failures in process management. - Deployment integration. Run
php artisan horizon:terminateafter every deployment. This signals Horizon to gracefully restart workers, picking up new code without dropping in-flight jobs. Add this to your Deployer or CI/CD pipeline post-deploy hook.
On sister sites sharing a Deployer 7 + GitLab CI pipeline, the horizon:terminate command runs in the post-deploy task. This ensures every release picks up code changes without manual intervention. If you’re using zero-downtime deployments with symlinked releases, verify that the terminate command targets the correct PHP binary and path; stale symlinks in cron or deploy scripts are a recurring production issue I’ve debugged more than once.
How Do You Optimize Laravel Horizon Performance Under Load?
Monitoring itself has cost. Horizon writes metrics to Redis on every job lifecycle event. Under extreme throughput (thousands of jobs per minute), this can saturate Redis connections or increase latency for application reads.
Mitigate this by tuning metrics.trim_recent and metrics.trim_snapshots in your config. Reduce snapshot frequency from the default 5 minutes to 15 or 30 minutes if you don’t need granular historical data. Trim recent job records aggressively; keeping 24 hours of individual job metrics is rarely necessary for operational debugging.
Tag jobs strategically. Horizon’s tag-based filtering is powerful for tracing user-specific or tenant-specific workflows, but excessive tagging increases Redis write volume. Tag by business entity (order ID, user ID, document reference) rather than generic labels. In legal-tech portals, tagging by case reference allows support staff to trace all jobs related to a specific matter without querying logs.
Finally, separate your monitoring Redis instance from your application cache if possible. Cache flushes should never wipe queue metrics. On projects where budget constrains infrastructure, use Redis databases (SELECT 0 for cache, SELECT 1 for queues/metrics) as a minimum isolation boundary. For clients needing guidance on infrastructure costs, understanding website development cost in Nepal helps frame trade-offs between dedicated Redis instances and shared-resource architectures.
Implementing Laravel Horizon to Monitor and Scale Your Queues Effectively
Laravel Horizon transforms queue management from opaque guesswork into observable, tunable infrastructure. By configuring environment-specific scaling, choosing the right balancing strategy, handling failures explicitly, and respecting deployment hygiene, you build systems that adapt to real workload patterns rather than static assumptions. The investment in proper Horizon setup pays dividends every time traffic spikes or a third-party API slows down.
If you’re implementing Laravel Horizon to monitor and scale your queues on a production system and need hands-on configuration, performance auditing, or deployment pipeline integration, get in touch. I work with teams worldwide and have specific experience tuning queue infrastructure for legal-tech, eCommerce, and API-heavy Laravel applications.

