Laravel Horizon gives you a real-time dashboard for your queue workers. It’s good for debugging. It is not a pager.
If you’re searching for Laravel Horizon monitoring, this page covers the signals that matter most: paused supervisors, queue depth, oldest job age, and failed-job spikes.
A Horizon supervisor can pause at 2am. Jobs can pile up to thousands. Customers can start seeing delayed responses. And the dashboard will only help if somebody is already looking at it.
What Horizon writes to Redis
Horizon stores its operational state in Redis using well-defined key patterns. The supervisor states, process counts, queue metrics, and job history are all there. Crontinel reads them directly, which means:
- No polling your application’s HTTP endpoints
- No Horizon API to authenticate against
- Works even when your web server is down (Redis is still up)
Signals Crontinel monitors
Supervisor status
Every Horizon supervisor has a status in Redis: running, paused, or stopped. Crontinel polls this for each supervisor and alerts when any supervisor moves out of the running state.
A paused supervisor is a common failure mode during deployments. A php artisan horizon:terminate that doesn’t restart cleanly leaves the supervisor paused. Crontinel catches this within seconds.
Queue depth
Crontinel reads the pending job count per queue and alerts when depth exceeds a configured threshold. You set separate thresholds per queue:
CRONTINEL_QUEUE_DEPTH_THRESHOLD=100 # default for all queues
CRONTINEL_QUEUE_DEPTH_INVOICES=25 # tighter threshold for billing queue
This lets you be aggressive about billing queue alerts without getting paged for a temporarily backed-up notification queue.
Oldest job age
A queue with 50 jobs and an oldest job age of 2 minutes is fine. A queue with 50 jobs and an oldest job age of 40 minutes is not. Crontinel tracks both and alerts when the oldest job age exceeds your threshold, separate from depth.
Failed job rate
Horizon tracks failed jobs in a sorted set. Crontinel computes a rolling failure rate (failures per minute over the last 5 minutes) and alerts when it exceeds a configured threshold. A spike in failures often indicates a broken external dependency before the queue depth metric catches it.
Setup
Crontinel’s Horizon monitoring activates automatically when Horizon is detected. The package checks for Horizon’s presence in vendor/ and starts reading Redis state on the same polling cycle as cron recording.
composer require crontinel/laravel
php artisan crontinel:install
Configure your thresholds in .env:
CRONTINEL_HORIZON_ENABLED=true
CRONTINEL_HORIZON_SUPERVISOR_ALERT=true
CRONTINEL_QUEUE_DEPTH_THRESHOLD=100
CRONTINEL_FAILED_JOBS_PER_MINUTE=5
Alert example
When a supervisor pauses, Crontinel fires an alert to your configured channel. A Slack message looks like:
[Crontinel] Horizon supervisor "default" is paused
App: production-api
Paused at: 2:14am UTC
Queue depth (emails): 847 jobs
Oldest job: 12 minutes
You get the supervisor name, the queue state at the time of the alert, and a direct link to your Crontinel dashboard.
Horizon monitoring vs Horizon dashboard
The Horizon dashboard shows you current state. It’s useful when you’re already debugging a problem. Crontinel tells you when a problem starts, so you’re not waiting for a user report before you check the dashboard.
These two tools serve different moments in the incident lifecycle. Use both.
Team access
Organization members can view connected apps in the shared dashboard when their access allows it. Engineers on call can check Horizon health without direct Redis access.
Common Horizon failure scenarios Crontinel catches
- Supervisor pauses after a
horizon:terminateduring deploy (common with Envoyer) - Redis memory pressure causes Horizon to stop processing new jobs
- An external API starts throttling, causing job failures to spike
- Queue depth builds up overnight when traffic is low and workers are insufficient
- A failed deployment leaves workers running old code that can’t deserialize new job payloads
All of these produce Redis state changes that Crontinel detects without any application-level ping or custom command.