Background Job Monitoring Setup (Sidekiq Dashboard / Bull Board / Flower)
Queue without monitoring—black box. Tasks hung, failed, accumulated thousands—learn about it from users. Queue dashboard solves: see each task status, failed attempts, queue wait time.
Laravel Horizon
For Laravel + Redis queues—standard tool. Installed as package, provides web interface, metrics API, fine-grained worker pool tuning.
composer require laravel/horizon
php artisan horizon:install
php artisan migrate
Worker configuration in config/horizon.php:
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['high', 'default', 'low'],
'balance' => 'auto',
'minProcesses' => 2,
'maxProcesses' => 10,
'tries' => 3,
'timeout' => 60,
],
'supervisor-media' => [
'connection' => 'redis',
'queue' => ['media', 'transcoding'],
'balance' => 'simple',
'processes' => 2,
'timeout' => 3600,
],
],
],
balance: auto—Horizon automatically scales processes based on queue depth.
Run Horizon:
php artisan horizon
In production via Supervisor:
[program:horizon]
command=php /var/www/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/horizon.log
Dashboard available at /horizon. By default local only. For production—configure HorizonServiceProvider::gate():
// app/Providers/HorizonServiceProvider.php
protected function gate(): void
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, config('horizon.admin_emails', []));
});
}
Metrics from Horizon via API—for external monitoring integration:
curl https://yoursite.com/horizon/api/stats \
-H "Cookie: laravel_session=..."
Bull Board (Node.js / BullMQ)
If backend on Node.js with BullMQ or Bull:
npm install @bull-board/express @bull-board/api bullmq
import { createBullBoard } from '@bull-board/api';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { ExpressAdapter } from '@bull-board/express';
import { Queue } from 'bullmq';
import express from 'express';
const emailQueue = new Queue('email', { connection: { host: 'localhost', port: 6379 } });
const mediaQueue = new Queue('media', { connection: { host: 'localhost', port: 6379 } });
const reportQueue = new Queue('reports',{ connection: { host: 'localhost', port: 6379 } });
const serverAdapter = new ExpressAdapter();
serverAdapter.setBasePath('/admin/queues');
createBullBoard({
queues: [
new BullMQAdapter(emailQueue),
new BullMQAdapter(mediaQueue),
new BullMQAdapter(reportQueue),
],
serverAdapter,
});
const app = express();
// Authorization middleware before dashboard
app.use('/admin/queues', (req, res, next) => {
const token = req.headers['x-admin-token'];
if (token !== process.env.ADMIN_TOKEN) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
}, serverAdapter.getRouter());
app.listen(3000);
Bull Board shows: active tasks, waiting, completed, failed. From failed tasks manually trigger retry.
Flower (Celery / Python)
For Python stack with Celery:
pip install flower
celery -A myapp flower --port=5555 --basic_auth=admin:secretpass
Or via Docker:
# docker-compose.yml
flower:
image: mher/flower:2.0
command: celery --broker=redis://redis:6379/0 flower --port=5555
environment:
- FLOWER_BASIC_AUTH=admin:secretpass
- FLOWER_URL_PREFIX=/flower
ports:
- "5555:5555"
depends_on:
- redis
Flower provides REST API for automation:
# All workers status
curl http://localhost:5555/api/workers
# Tasks in queue
curl http://localhost:5555/api/tasks
# Revoke task
curl -X POST http://localhost:5555/api/task/revoke/{task_id}?terminate=true
Alerting on Queue Buildup
Horizon—configure notifications on threshold exceed:
// config/horizon.php
'waits' => [
'redis:default' => 60, // alert if task waits > 60 sec
'redis:high' => 10,
'redis:transcoding'=> 300,
],
Custom Telegram/Slack integration—via Horizon event listener:
// app/Providers/EventServiceProvider.php
use Laravel\Horizon\Events\LongWaitDetected;
protected $listen = [
LongWaitDetected::class => [
\App\Listeners\NotifyOnLongQueueWait::class,
],
];
// app/Listeners/NotifyOnLongQueueWait.php
class NotifyOnLongQueueWait
{
public function handle(LongWaitDetected $event): void
{
$message = "Queue alert: `{$event->queue}` wait time {$event->wait}s exceeds threshold";
Http::post(config('services.telegram.webhook_url'), [
'chat_id' => config('services.telegram.admin_chat_id'),
'text' => $message,
'parse_mode' => 'Markdown',
]);
}
}
Metrics for Prometheus / Grafana
Using Prometheus stack, Laravel Horizon exports metrics via spatie/laravel-prometheus package:
composer require spatie/laravel-prometheus
Or write custom endpoint:
Route::get('/metrics', function () {
$stats = app(\Laravel\Horizon\Contracts\MetricsRepository::class);
$output = "# HELP horizon_queue_size Queue depth\n";
$output .= "# TYPE horizon_queue_size gauge\n";
foreach (['default', 'high', 'low', 'media'] as $queue) {
$size = \Illuminate\Support\Facades\Redis::llen("queues:{$queue}");
$output .= "horizon_queue_size{queue=\"{$queue}\"} {$size}\n";
}
return response($output, 200, ['Content-Type' => 'text/plain']);
})->middleware('throttle:60,1');
Timeline
Install Horizon/Bull Board/Flower, basic worker pool configuration—3–4 hours. Configure dashboard auth, Slack/Telegram alerting—another 2–3 hours. Prometheus/Grafana integration—separate, 4–6 hours.







