ojbaeza / station
A comprehensive Laravel queue management package with multi-driver support (RabbitMQ, Redis, SQS, Beanstalkd, Kafka), job recovery, and real-time dashboard
Fund package maintenance!
ojbaeza
Installs: 17
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/ojbaeza/station
Requires
- php: ^8.3
- ext-pcntl: *
- ext-posix: *
- illuminate/bus: ^11.0 || ^12.0
- illuminate/contracts: ^11.0 || ^12.0
- illuminate/database: ^11.0 || ^12.0
- illuminate/queue: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
- ramsey/uuid: ^4.7
Requires (Dev)
- aws/aws-sdk-php: ^3.0
- friendsofphp/php-cs-fixer: ^3.64
- inertiajs/inertia-laravel: ^2.0
- larastan/larastan: ^3.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0 || ^10.0
- pda/pheanstalk: ^8.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^12
Suggests
- ext-amqp: Required to use RabbitMQ driver
- ext-rdkafka: Required to use Apache Kafka driver
- ext-sockets: Required for some queue drivers
- aws/aws-sdk-php: Required to use Amazon SQS driver (^3.0)
- inertiajs/inertia-laravel: Required for the Station dashboard (^2.0)
- pda/pheanstalk: Required to use Beanstalkd driver (^8.0)
README
A Laravel Horizon alternative with multi-driver support and enhanced job recovery.
Station replaces Horizon as your queue management and monitoring layer. Your existing Laravel queue code works unchanged — the only difference is running php artisan station:work instead of php artisan horizon.
Station hooks into Laravel's queue event system to transparently track every job, batch, and workflow through a real-time dashboard.
Resources: Contributing | Security Policy | Changelog | Upgrade Guide
What Station Provides
- Multi-driver support — RabbitMQ, Redis, SQS, Beanstalkd, Kafka
- Seamless job tracking — Every
dispatch()call is recorded automatically - Batch monitoring — Real-time progress for
Bus::batch()with integer failure thresholds - Workflow engine — Multi-step DAG workflows with conditions, branching, and pause/resume
- Job recovery — Checkpointing for long-running jobs, stuck job detection, automatic recovery
- Real-time dashboard — Inertia.js + Vue 3 UI with 3-second auto-refresh
- Built-in observability — Prometheus metrics, health checks, multi-channel alerting (Slack, Discord, Teams, email, webhooks)
- Alerting — Configurable alert rules with multi-channel notifications (Slack, Discord, Teams, email, webhooks, log)
- Security — Job encryption, payload masking, audit logging, gate-based authorization
For a detailed comparison with Horizon, see Station vs Horizon.
Requirements
- PHP 8.3+ (8.4 recommended)
- Laravel 11.x or 12.x
- ext-pcntl, ext-posix
Driver-specific: ext-amqp (RabbitMQ), ext-redis or predis (Redis), aws/aws-sdk-php (SQS), pda/pheanstalk (Beanstalkd), ext-rdkafka (Kafka)
Dashboard: inertiajs/inertia-laravel (optional — required only for the web dashboard)
Installation
composer require ojbaeza/station
php artisan vendor:publish --provider="Station\StationServiceProvider"
php artisan migrate
php artisan station:install
Set your queue connection in .env:
QUEUE_CONNECTION=station RABBITMQ_HOST=localhost RABBITMQ_PORT=5672 RABBITMQ_USER=your-rabbitmq-username RABBITMQ_PASSWORD=your-secure-password RABBITMQ_VHOST=/
Start the supervisor:
php artisan station:work
Access the dashboard at /station.
Jobs
Standard Laravel dispatch is tracked automatically — no code changes needed:
// All of these are tracked by Station ProcessOrderJob::dispatch($order); ProcessOrderJob::dispatch($order)->onQueue('high'); ProcessOrderJob::dispatch($order)->delay(now()->addMinutes(5));
Station also provides a fluent API with tags and priority:
use Station\Facades\Station; Station::job(new ProcessOrderJob($order)) ->onQueue('high') ->delay(now()->addMinutes(5)) ->tags(['orders', 'payments']) ->dispatch();
Station records processing time, memory usage, attempts, wait time, and status for every job.
Batches
Station wraps Laravel's native Bus::batch() with enhanced tracking. Jobs must use the Batchable trait:
use Illuminate\Bus\Batchable; class ProcessOrderJob implements ShouldQueue { use Batchable, Dispatchable, InteractsWithQueue, Queueable; public function handle(): void { if ($this->batch()?->cancelled()) { return; } // Do work... } }
Create and monitor batches:
use Station\Facades\Batch; $batch = Batch::create( jobs: [ new ProcessOrderJob($order1), new ProcessOrderJob($order2), new ProcessOrderJob($order3), ], name: 'Daily Orders', allowedFailures: 2, // Allow up to 2 jobs to fail (integer, not boolean) ); // Check progress $batch = Batch::find($batch->id); $batch->progress(); // Percentage (0-100) // Operations Batch::cancel($batch->id); // Cancel remaining jobs Batch::retryFailed($batch->id); // Retry failed jobs
For details on the overlay table strategy and atomic counter tracking, see Batches Architecture.
Workflows
Simple Workflows
A DAG wrapper around Bus::batch() — define steps with dependencies, Station resolves execution order:
use Station\Core\Workflow; Workflow::create('order-pipeline') ->add('validate', new ValidateOrderJob($order)) ->add('payment', new ProcessPaymentJob($order), ['validate']) ->add('inventory', new ReserveInventoryJob($order), ['validate']) ->add('ship', new ShipOrderJob($order), ['payment', 'inventory']) ->onQueue('high') ->dispatch();
Steps with no dependencies between them run in parallel.
Full Workflow Engine
Persistent workflows with conditional steps, branching, and pause/resume:
use Station\Facades\Workflow; // Define a reusable workflow Workflow::define('payment-processing') ->addStep('validate', ValidatePaymentJob::class) ->addStep('charge', ChargeCardJob::class, ['validate']) ->addConditionalStep('notify', SendNotificationJob::class, fn($context) => $context['charge_successful'] ?? false, ['charge'] ) ->timeout(3600); // Run synchronously $instance = Workflow::run('payment-processing', [ 'amount' => 99.99, 'user_id' => 42, ]); // Or run asynchronously (recommended for production) $instance = Workflow::runAsync('payment-processing', [ 'amount' => 99.99, 'user_id' => 42, ]);
Workflow instances are stored in the database with full state tracking, and can be paused, resumed, or cancelled from the dashboard.
For details on execution model, branching, and context propagation, see Workflows Architecture.
Workflows vs Laravel Bus
Station's workflow engine provides DAG orchestration beyond what Laravel's built-in Bus::batch() and Bus::chain() can express:
| Feature | Station Workflows | Bus::batch() | Bus::chain() |
|---|---|---|---|
| DAG dependencies | Yes | No | No (linear only) |
| Parallel execution | Yes (async) | Yes | No |
| Conditional steps | Yes (runtime) | No | No |
| Dynamic branching | Yes | No | No |
| Pause / Resume | Yes | No | No |
| Context passing between steps | Yes | No | No |
| Per-step status tracking | Yes | Batch-level only | No |
| Per-step retry / timeout | Yes (async) | Per-job | Per-job |
| Progress tracking | Yes (step %) | Yes (job count %) | No |
| Cancellation | Yes | Yes | Stops chain |
| Stuck step recovery | Yes | No | No |
| Persisted state | Yes (DB) | Yes (job_batches) | No |
Station workflows support both synchronous (Workflow::run()) and asynchronous (Workflow::runAsync()) execution. In async mode, each step is dispatched as a standard ShouldQueue job processed by Laravel workers, enabling true parallel execution across multiple workers. The orchestration layer (dependency resolution, conditional evaluation, context propagation) is where Station adds value on top of Laravel's primitives.
Job Recovery
Checkpointing
Long-running jobs can save progress and resume after failures:
use Station\Contracts\Checkpointable; class ImportUsersJob implements ShouldQueue, Checkpointable { private int $lastId = 0; public function handle(): void { User::where('id', '>', $this->lastId)->orderBy('id') ->chunk(100, function ($users) { foreach ($users as $user) { $this->processUser($user); } $this->lastId = $users->last()->id; }); } public function checkpoint(): array { return ['last_id' => $this->lastId]; } public function restore(array $data): void { $this->lastId = $data['last_id'] ?? 0; } public function hasMoreWork(): bool { return User::where('id', '>', $this->lastId)->exists(); } }
Stuck Job Detection
Station detects hung jobs using weighted scoring (heartbeat, runtime, memory, process state) and can recover them automatically:
php artisan station:recover
php artisan station:recover --dry-run # Preview without acting
php artisan station:recover --strategy=graceful --workflows
For details on scoring, strategies, and health checks, see Recovery System.
Artisan Commands
| Command | Description |
|---|---|
station:work |
Start the supervisor (manages multiple workers) |
station:status |
Show queue and worker status |
station:pause {queue} |
Pause processing for a specific queue |
station:resume {queue} |
Resume a paused queue |
station:terminate |
Gracefully stop all workers |
station:recover |
Detect and recover stuck jobs (--strategy, --threshold, --dry-run, --workflows) |
station:retry {id} |
Retry a failed job (--all for all) |
station:failed |
List failed jobs |
station:flush |
Delete all failed jobs |
station:prune |
Clean up old data |
station:health |
Run health checks |
station:install |
Install Station |
station:publish-supervisor |
Generate a Supervisor config file (--workers, --user, --path) |
station:alerts:check |
Evaluate alert rules and send notifications (--seed to seed from config) |
Dashboard
Real-time monitoring UI at /station with auto-refresh every 3 seconds:
- Overview — Job throughput, failure rate, average processing time, active workers
- Jobs — Paginated list with status/queue/class filters, retry and delete actions
- Failed Jobs — Exception details, retry individually or in bulk
- Batches — Progress bars, status tracking, cancel and retry
- Workflows — Definitions, running instances, step-by-step progress
- Monitoring — Queue depths, worker counts, throughput charts
- Metrics — Historical data with time range selection
Authorization
By default, the dashboard is accessible in local environment. In other environments, configure the authorization gate in config/station.php:
// config/station.php 'dashboard' => [ 'authorization' => 'viewStation', // Gate name to check ],
Then define the gate in your AuthServiceProvider:
Gate::define('viewStation', function ($user) { return in_array($user->email, ['admin@example.com']); });
API
Full REST API at /api/station/ (configurable) with Bearer token auth. See docs/openapi.yaml for the OpenAPI 3.1 specification.
Configuration
// config/station.php (key sections) return [ 'default' => 'rabbitmq', 'connections' => [ 'rabbitmq' => [ 'driver' => 'rabbitmq', 'hosts' => [[ 'host' => env('RABBITMQ_HOST', 'localhost'), /* ... */ ]], ], ], 'supervisors' => [ 'default' => [ 'queues' => ['default'], 'processes' => 2, 'timeout' => 60, 'memory' => 128, ], ], 'dashboard' => ['enabled' => true, 'path' => 'station', 'middleware' => ['web', 'auth']], 'recovery' => ['enabled' => true, 'stuck_job_timeout' => 900, 'auto_resume' => true], 'checkpoints' => ['enabled' => true, 'storage' => 'database'], ];
For the full configuration reference, see Configuration.
Documentation
For in-depth documentation, see the docs/ directory:
- Architecture — Internal architecture: jobs, batches, workflows, workers, recovery, metrics, database schema, Station vs Horizon
- Queue Drivers — Feature matrix, per-driver configs, known limitations
- Facades — All 7 facades with every public method
- Security & Resilience — API auth, encryption, masking, alerting, circuit breaker
- Configuration — Full config reference and environment variables
- Migrating from Horizon — Step-by-step migration guide and feature mapping
- API Reference — OpenAPI 3.1 specification
Development Setup
Station includes a Docker environment with all queue drivers pre-configured:
docker compose up -d # Start all services make test # Run tests make quality # Run all quality checks (tests + PHPStan + code style)
See CONTRIBUTING.md for the full development guide, docker/README.md for Docker environment details, and the Makefile for all available commands.
Contributing
We welcome contributions! Please see CONTRIBUTING.md for development setup, code style guidelines (PER-CS 3.0), and testing requirements (95% coverage).
Security
For security vulnerabilities, please see SECURITY.md. Do not report security issues via GitHub Issues.
License
Station is open-sourced software licensed under the MIT license.







