- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Laravel Horizon in Production: Queue Monitoring That Actually Tells You Something
About Post
Queues fail quietly. That's their whole personality.
A web request that breaks shows an error page, and someone complains within minutes. A queued job that breaks just... doesn't happen. The welcome email never arrives. The PDF is never generated. The payment webhook sits unprocessed. And you find out days later from a confused user, not from your monitoring.
If your Laravel app runs on Redis queues, Horizon is the tool that turns that silence into something you can see. Installing it takes minutes. Setting it up so it actually protects you in production takes a little more thought, so here's the setup I'd use, organised around the four questions you need answered at any moment.
Question 1: are the workers actually running?
Horizon is a long-running process. It starts and supervises your queue workers, so if Horizon itself dies, everything stops. The first job is making sure something restarts it.
On a Linux server that's usually Supervisor (the process manager, not to be confused with Horizon's own "supervisors"):
[program:horizon]
process_name=%(program_name)s
command=php /var/www/app/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/horizon.log
stopwaitsecs=3600
stopwaitsecs should be longer than your longest job, so a restart waits for running jobs to finish instead of killing them halfway.
Then, in your deploy script, after the new code is in place:
php artisan horizon:terminate
Workers keep your application in memory. Without this line, they keep running yesterday's code after a deploy, which produces some of the most confusing bugs you'll ever chase. horizon:terminate lets them finish their current job and exit, and Supervisor starts fresh ones.
For an external health check, php artisan horizon:status tells you whether Horizon is running, paused or inactive.
Question 2: are the queues keeping up?
This is what config/horizon.php is for. The part that matters most is the supervisor configuration per environment. A setup I like separates urgent work from slow work, so a batch of heavy reports can never delay a payment confirmation:
'environments' => [
'production' => [
'critical' => [
'connection' => 'redis',
'queue' => ['payments', 'notifications'],
'balance' => 'auto',
'minProcesses' => 2,
'maxProcesses' => 8,
'tries' => 3,
'timeout' => 30,
],
'background' => [
'connection' => 'redis',
'queue' => ['default', 'reports'],
'balance' => 'auto',
'maxProcesses' => 4,
'tries' => 2,
'timeout' => 300,
],
],
],
A few settings worth understanding rather than copying:
balance:automoves worker processes towards the busiest queues,simplesplits them evenly, andfalseworks through queues in the order listed.minProcesses/maxProcesses: the floor and ceiling for auto-balancing. The minimum keeps critical queues warm even when they're quiet.timeout: must be shorter than theretry_aftervalue of your Redis connection inconfig/queue.php. If it isn't, a slow job can be picked up a second time while the first attempt is still running. That's a classic source of duplicate emails and double processing.
Then tell Horizon what "falling behind" means for you:
'waits' => [
'redis:payments' => 30,
'redis:default' => 120,
],
If a job waits longer than that number of seconds, Horizon fires a LongWaitDetected event. On its own, nobody sees it. Which brings us to notifications.
Question 3: will someone hear about it?
A dashboard is only useful if someone is looking at it. Route Horizon's alerts somewhere people already look, in the boot method of HorizonServiceProvider:
Horizon::routeMailNotificationsTo('[email protected]');
Horizon::routeSlackNotificationsTo(config('services.slack.horizon_webhook'), '#alerts');
And turn on the metrics graphs, which need a regular snapshot. In routes/console.php:
Schedule::command('horizon:snapshot')->everyFiveMinutes();
Without this, the Metrics tab stays empty, and it's one of the most useful screens: throughput and runtime per job and per queue. A job whose runtime creeps up week after week is telling you something about a growing table or a slow external API long before it times out.
Question 4: what failed, and why?
The Failed Jobs screen shows the exception, the stack trace and the job's payload, and lets you retry. That retry button is wonderful and slightly dangerous: retrying a job that isn't idempotent can send the same notification twice. Know which of your jobs are safe to replay before you click it in a hurry.
Two features make this screen much more useful:
- Tags. Horizon automatically tags jobs with the Eloquent models they receive, like
App\Models\Invoice:4821. When a user reports a problem with one record, search the tag and see every job that touched it. - Retention. The
trimsection of the config controls how long recent, completed and failed jobs are kept (in minutes). Keep failed jobs long enough to survive a weekend.
The production rule: every queue needs an owner, a "too slow" threshold and an alert that reaches a human. A dashboard nobody opens is just a nicer way to fail silently.
Lock the door
Horizon's dashboard shows job payloads, which can include emails, names and IDs. Outside the local environment, access is controlled by the viewHorizon gate in HorizonServiceProvider:
protected function gate(): void
{
Gate::define('viewHorizon', function ($user) {
return $user->hasRole('developer');
});
}
(Adapt hasRole to however your app handles roles.) Also think about what you put in job payloads in the first place. Pass IDs, not whole documents or secrets.
Your Horizon checklist
- Supervisor restarts Horizon; deploys run
horizon:terminate. - Urgent and slow queues run under separate supervisors.
- Every
timeoutis below the connection'sretry_after. waitsthresholds are set and alerts go to mail or Slack.horizon:snapshotis scheduled, so metrics exist.- The dashboard is behind the
viewHorizongate. - You know which jobs are safe to retry.
Horizon only works with Redis queues. If you're on the database or SQS driver, the same four questions still apply; you'll just answer them with other tools.
What's the first metric you look at when a queue starts misbehaving: wait time, failures or job runtime?

Be first to comment it...