Job chaining and batching (Bus::chain, Bus::batch)
Concept
Laravel Horizon is the official Redis queue monitoring dashboard. It provides real-time metrics, failed job inspection, and automatic worker management.
What Horizon adds over bare queue:work:
- Web dashboard at
/horizon— real-time throughput graphs, failed jobs, pending jobs. - Auto-scaling workers — defines min/max processes per queue, scales based on load.
- Job tagging — jobs with Eloquent models are tagged with model ID for searchability.
- Job retention — recent job history stored in Redis for inspection.
- Metrics — tracks job runtime, throughput, failure rate.
Installation:
composer require laravel/horizon
php artisan horizon:install
php artisan migrateWorker management: In config/horizon.php, define supervisor configurations per environment. Horizon starts the configured number of workers and auto-balances them across queues.
Running: php artisan horizon — starts Horizon (replaces multiple queue:work commands).
Deployment: php artisan horizon:terminate before deploy. Process manager (Supervisor/systemd) auto-restarts it. New workers pick up the new code.
Pausing: php artisan horizon:pause / horizon:continue for maintenance.
Tagging: Jobs automatically tag their model arguments. ProcessOrder for Order{id:42} is tagged App\Models\Order:42. Searchable in the dashboard.
Code Example
# Installation and setup
# composer require laravel/horizon
# php artisan horizon:install
# php artisan migrate
// config/horizon.php — worker configuration
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['high', 'default', 'low'],
'balance' => 'auto', // auto, simple, false
'autoScalingStrategy' => 'time', // 'time' or 'size'
'maxProcesses' => 20,
'minProcesses' => 2,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
'memory' => 128,
'tries' => 3,
'timeout' => 60,
'nice' => 0,
],
'supervisor-email' => [
'connection' => 'redis',
'queue' => ['emails'],
'balance' => 'simple',
'maxProcesses' => 5,
'minProcesses' => 1,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
'tries' => 1,
],
],
],
// Authorization — who can view /horizon
// app/Providers/HorizonServiceProvider.php
\Laravel\Horizon\Horizon::auth(function ($request) {
return auth()->check() && auth()->user()->isAdmin();
});
// Custom tags on jobs
class ProcessOrder implements \Illuminate\Contracts\Queue\ShouldQueue
{
use \Illuminate\Bus\Queueable, \Illuminate\Foundation\Bus\Dispatchable;
use \Illuminate\Queue\InteractsWithQueue, \Illuminate\Queue\SerializesModels;
public function __construct(private readonly \App\Models\Order $order) {}
// Custom tags for Horizon search
public function tags(): array
{
return [
"order:{$this->order->id}",
"user:{$this->order->user_id}",
"status:{$this->order->status}",
];
}
public function handle(): void { ... }
}
// Supervisor/systemd to keep Horizon running
// [program:laravel-horizon]
// command=php /var/www/artisan horizon
// autostart=true
// autorestart=true
// user=www-data