nilanjank / scheduler-hub-laravel
A Laravel scheduled-task dashboard with persistent run history and failure notifications (mail, Slack, webhook).
Package info
github.com/nilanjan-k/scheduler-hub-laravel
pkg:composer/nilanjank/scheduler-hub-laravel
Requires
- php: ^8.2
- illuminate/console: ^11.0||^12.0||^13.0
- illuminate/contracts: ^11.0||^12.0||^13.0
- illuminate/database: ^11.0||^12.0||^13.0
- illuminate/http: ^11.0||^12.0||^13.0
- illuminate/notifications: ^11.0||^12.0||^13.0
- illuminate/support: ^11.0||^12.0||^13.0
Requires (Dev)
- laravel/pint: ^1.14
- orchestra/testbench: ^9.0||^10.0||^11.0
- pestphp/pest: ^2.0||^3.0
README
🛰️ Scheduler Hub
A control center for Laravel's task scheduler.
See every scheduled task at a glance, trigger runs on demand, and never lose track of what happened — with a persistent run history and real failure alerts.
Why Scheduler Hub
Most Laravel scheduler dashboards stop at "here's a list of your cron jobs." Scheduler Hub goes further:
| 🗂️ Persistent run history | Every scheduled run — not just manual ones — is recorded automatically by hooking into Laravel's native scheduler events. History stays accurate even if nobody ever opens the dashboard. |
| 🔔 Failure notifications | Mail, Slack, and signed generic webhooks, each independently toggleable. Know the moment something breaks, wherever your team already looks. |
| 🆔 Stable task identity | Tasks are identified by a hash of their command, expression, timezone, and description — not their position in the schedule list — so the "Run" button always hits the task you meant, even if the schedule changes between page load and click. |
| 🎨 Clean, fast dashboard | Live search, type filters, status badges, and an in-browser output viewer. No build step, no JS framework — just a single Blade view. |
| 🔒 Locked down by default | Disabled out of the box. Gate-based or callback-based authorization, configurable middleware, and manual execution off unless you explicitly turn it on. |
Requirements
| Laravel | PHP |
|---|---|
| 11.x | 8.2+ |
| 12.x | 8.2+ |
| 13.x | 8.3+ |
Installation
composer require nilanjank/scheduler-hub-laravel php artisan vendor:publish --tag=scheduler-hub-config php artisan migrate
Quick start
// routes/console.php use Illuminate\Support\Facades\Schedule; Schedule::command('inspire') ->everyMinute() ->description('Displays a random motivational quote.');
SCHEDULER_HUB_ENABLED=true
Visit http://localhost:8000/scheduler-hub 🎉
To allow the Run now button:
SCHEDULER_HUB_MANUAL_EXECUTION=true
The Tasks tab lists every registered task with its next run and, if history is on, its most recent run status. The History tab shows every recorded run, filterable by status, with full output/error on click.
Configuration
Everything lives in config/scheduler-hub.php:
'enabled' => env('SCHEDULER_HUB_ENABLED', false), 'path' => env('SCHEDULER_HUB_PATH', 'scheduler-hub'), 'middleware' => ['web', 'auth'], 'ability' => env('SCHEDULER_HUB_ABILITY', 'viewSchedulerHub'), 'authorize' => null, 'manual_execution' => env('SCHEDULER_HUB_MANUAL_EXECUTION', false), 'output_limit' => env('SCHEDULER_HUB_OUTPUT_LIMIT', 12000), 'history' => [ 'enabled' => true, 'track_scheduled_runs' => true, 'retention_days' => 30, 'per_page' => 25, ], 'notifications' => [ 'enabled' => false, 'notify_on' => ['failure'], 'channels' => [ 'mail' => ['enabled' => false, 'to' => null], 'slack' => ['enabled' => false, 'webhook_url' => null], 'webhook' => ['enabled' => false, 'url' => null, 'secret' => null], ], ],
🔒 Security
The dashboard is disabled by default. To use it in production:
SCHEDULER_HUB_ENABLED=true SCHEDULER_HUB_MANUAL_EXECUTION=false
The default Gate only allows access in the local environment. Define it
yourself for anything else:
use Illuminate\Support\Facades\Gate; Gate::define('viewSchedulerHub', fn ($user) => $user->is_admin);
Or use the authorize config callback instead of a Gate:
'authorize' => fn (\Illuminate\Http\Request $request) => $request->user()?->is_admin === true,
⚠️ Never mount this behind only
['web']middleware on a public app.
🔔 Notifications
Enable the channels you want:
SCHEDULER_HUB_NOTIFICATIONS_ENABLED=true SCHEDULER_HUB_NOTIFY_MAIL_ENABLED=true SCHEDULER_HUB_NOTIFY_MAIL_TO="ops@example.com,alerts@example.com" SCHEDULER_HUB_NOTIFY_SLACK_ENABLED=true SCHEDULER_HUB_NOTIFY_SLACK_WEBHOOK_URL="https://hooks.slack.com/services/…" SCHEDULER_HUB_NOTIFY_WEBHOOK_ENABLED=true SCHEDULER_HUB_NOTIFY_WEBHOOK_URL="https://example.com/hooks/scheduler" SCHEDULER_HUB_NOTIFY_WEBHOOK_SECRET="a-long-random-string"
Mail and Slack fire on failure by default (notify_on). The generic
webhook always fires for whichever statuses are in notify_on, so it can
double as a success ping too if you add success to that list.
Webhook requests are signed with an X-Scheduler-Hub-Signature header
(HMAC-SHA256 of the raw JSON body) whenever a secret is configured — verify
it on the receiving end before trusting the payload.
🗑️ Run history retention
Old runs don't accumulate forever — schedule the built-in prune command:
// routes/console.php use Illuminate\Support\Facades\Schedule; Schedule::command('scheduler-hub:prune')->daily();
Retention window is controlled by history.retention_days in the config.
License
Released under the MIT License.