Laravel Bee Queue
A simple, fast, Redis-backed job queue for Laravel — a PHP port of bee-queue (Node.js).
Jobs pushed from JS bee-queue can be consumed by the PHP worker and vice versa — they share the exact same Redis data layout.
#Installation
composer require g4t/laravel-bee-queue php artisan vendor:publish --tag=bee-queue-config php artisan vendor:publish --tag=bee-queue-assets
bee-queue-assetscopies the dashboard logo intopublic/vendor/bee-queue/. Run this once after install.
#Configuration
Publish the config file and set your .env keys:
| Key | Default | Description |
|---|---|---|
BEE_QUEUE_DEFAULT |
default |
Default queue name |
BEE_QUEUE_REDIS_CONNECTION |
default |
Redis connection from config/database.php |
BEE_QUEUE_PREFIX |
bq |
Redis key prefix |
BEE_QUEUE_CONCURRENCY |
1 |
Worker concurrency |
BEE_QUEUE_TIMEOUT |
60 |
Job timeout in seconds |
BEE_QUEUE_STALL_INTERVAL |
5000 |
Stall check interval in ms |
BEE_QUEUE_RETRY_ATTEMPTS |
3 |
Max retry attempts |
BEE_QUEUE_RETRY_BACKOFF |
fixed |
fixed or exponential |
BEE_QUEUE_RETRY_DELAY |
5 |
Retry delay in seconds |
BEE_QUEUE_REMOVE_ON_SUCCESS |
false |
Delete job data after success |
BEE_QUEUE_REMOVE_ON_FAILURE |
false |
Delete job data after failure |
BEE_QUEUE_DASHBOARD_PATH |
bee-queue |
URL path for the dashboard |
#Retry backoff strategies
fixed— waits the sameBEE_QUEUE_RETRY_DELAYseconds between every attempt.exponential— doubles the delay on each attempt:delay × 2^(attempt - 1). Useful when downstream services need time to recover.
#Basic Usage
#Creating & enqueuing a job
use G4T\BeeQueue\Facades\BeeQueue; BeeQueue::createJob(['user_id' => 42, 'action' => 'send_welcome_email']) ->retries(3) ->backoff('exponential') // 'fixed' or 'exponential' ->retryDelay(5) ->delay(30) // run 30 seconds from now ->timeout(60) ->save(); // On a named queue BeeQueue::queue('emails') ->createJob(['to' => 'alice@example.com']) ->save();
#Processing Jobs
#Option 1 — Closure handler (quick/inline)
$worker = BeeQueue::worker(); $worker->process(function ($job) { // $job->data contains your payload $job->reportProgress(50); // do work... $job->reportProgress(100); });
#Option 2 — Handler class with --handler flag
Create a handler class implementing JobContract:
// app/Jobs/ProcessMessage.php namespace App\Jobs; use G4T\BeeQueue\Contracts\JobContract; use G4T\BeeQueue\Job; class ProcessMessage implements JobContract { public function __construct(private Job $job) {} public function handle(): void { $data = $this->job->data; // process $data... } }
Run the worker with it:
php artisan bee-queue:work --handler=App\\Jobs\\ProcessMessage php artisan bee-queue:work emails --handler=App\\Jobs\\ProcessMessage
This handler receives every job on that queue regardless of data content — ideal for jobs pushed from JS bee-queue.
#Option 3 — Auto-resolve handler from job data
Include a class key when pushing from PHP:
BeeQueue::createJob([ 'class' => App\Jobs\ProcessMessage::class, 'text' => 'hello', ])->save();
Run the worker without --handler — it will auto-resolve the class:
php artisan bee-queue:work
#Routing multiple job types in one handler
public function handle(): void { match ($this->job->data['action'] ?? null) { 'send_email' => $this->sendEmail(), 'resize_image' => $this->resizeImage(), default => \Log::warning('Unknown action', $this->job->data), }; }
#Artisan Commands
# Start a worker (runs forever) php artisan bee-queue:work # Start on a named queue php artisan bee-queue:work emails # With a specific handler class php artisan bee-queue:work --handler=App\\Jobs\\ProcessMessage # Process one job and exit php artisan bee-queue:work --once # Show queue health stats php artisan bee-queue:stats php artisan bee-queue:stats emails
#Dashboard
The package ships with a built-in web dashboard for monitoring and managing your queues.
#Setup
# Publish the dashboard logo asset (required once) php artisan vendor:publish --tag=bee-queue-assets
#Accessing the dashboard
Visit /bee-queue in your browser (or whatever path you set via BEE_QUEUE_DASHBOARD_PATH).
#Features
- Stats cards — live counts for waiting, active, succeeded, failed, and delayed jobs. Click any card to filter the job list.
- Job table — shows job ID, status badge, payload data, timestamp, and progress bar.
- Retry — re-queues a failed job back to the waiting list with a fresh timestamp.
- Delete — removes a job from all Redis keys permanently.
- Auto-refresh — page reloads every 5 seconds with a countdown timer.
- Queue switcher — switch between named queues from the header input.
#Middleware & path
Configure the dashboard in config/bee-queue.php:
'dashboard' => [ 'path' => env('BEE_QUEUE_DASHBOARD_PATH', 'bee-queue'), 'middleware' => ['web'], // add 'auth' or your own middleware here ],
To protect the dashboard behind authentication:
'middleware' => ['web', 'auth'],
#Events
Listen to these events in your EventServiceProvider:
| Event | Properties | Description |
|---|---|---|
G4T\BeeQueue\Events\JobSucceeded |
$job |
Job completed successfully |
G4T\BeeQueue\Events\JobFailed |
$job, $exception |
Job exhausted all retries |
G4T\BeeQueue\Events\JobRetrying |
$job, $exception, $nextAttempt |
Job is being retried |
G4T\BeeQueue\Events\JobProgress |
$job, $progress |
Job reported progress |
use G4T\BeeQueue\Events\JobFailed; use G4T\BeeQueue\Events\JobSucceeded; Event::listen(JobSucceeded::class, function ($event) { \Log::info('Job done', ['id' => $event->job->id]); }); Event::listen(JobFailed::class, function ($event) { \Log::error('Job failed', [ 'id' => $event->job->id, 'error' => $event->exception->getMessage(), ]); });
#Cross-compatibility with JS bee-queue
This package uses the exact same Redis data layout as the Node.js bee-queue library, so you can mix producers and consumers across languages:
// Node.js — push a job const Queue = require('bee-queue'); const queue = new Queue('default'); queue.createJob({ text: 'Hello from Node' }).save();
# PHP — consume it php artisan bee-queue:work --handler=App\\Jobs\\ProcessMessage
#Redis Data Layout
bq:{queue}:id — STRING — auto-increment job ID counter
bq:{queue}:jobs — HASH — field=id, value=JSON job payload
bq:{queue}:waiting — LIST — IDs of jobs waiting to be processed
bq:{queue}:active — LIST — IDs of jobs currently being processed
bq:{queue}:succeeded — SET — IDs of successfully completed jobs
bq:{queue}:failed — SET — IDs of failed jobs
bq:{queue}:delayed — ZSET — IDs of delayed jobs (scored by run-at timestamp)
bq:{queue}:stallBlock — STRING — TTL-based lock for stall detection
Each job is stored as a JSON string in the jobs hash:
{ "data": { "your": "payload" }, "options": { "timestamp": 1779186989503, "stacktraces": [], "retries": 3, "backoff": "exponential", "retryDelay": 5, "timeout": 60, "delay": null }, "status": "succeeded", "progress": 0 }
#License
MIT