fanat98/laravel-task-orchestrator
Laravel Task Orchestrator is a lightweight dashboard for orchestrating Artisan command workflows with dependencies, pipelines, scheduling, queue health checks, stale run recovery and real-time monitoring.
It is built for Laravel teams that already use Artisan commands for imports, sync jobs, maintenance workflows or operational tasks, but need a clearer way to run them, monitor them and connect them into reliable workflows.
Instead of hiding important operational work inside cron entries or one-off commands, the package gives developers and operators a central place to see what can run, what is running, what failed and which downstream tasks should run next.
Use this package when your Laravel application has Artisan commands that:
discovery.php configuration fileInstall the package with Composer:
composer require fanat98/laravel-task-orchestrator
Publish the configuration and frontend assets:
php artisan vendor:publish --tag=task-orchestrator-config
php artisan vendor:publish --tag=task-orchestrator-assets
Run the package migrations:
php artisan migrate
The dashboard is available at:
/task-orchestrator
The path can be changed with route_prefix in config/task-orchestrator.php.
Create the discovery file configured by config/task-orchestrator.php:
mkdir -p app/TaskOrchestrator
touch app/TaskOrchestrator/discovery.php
Add one command to app/TaskOrchestrator/discovery.php:
<?php
return [
'commands' => [
'reports:send-daily' => [
'name' => 'send-daily-reports',
'label' => 'Send daily reports',
'group' => 'Reports',
'queue' => 'default',
'schedule' => [
'expression' => '0 8 * * *',
'human' => 'Daily at 08:00',
],
'timeout_minutes' => 10,
],
],
];
Make sure Laravel's scheduler and at least one queue worker are running:
php artisan schedule:work
php artisan queue:work
For production, run the scheduler from cron and manage queue workers with Supervisor, systemd, Laravel Horizon or your platform's worker process manager.
The package configuration lives in:
config/task-orchestrator.php
The most commonly changed options are:
return [
'route_prefix' => 'task-orchestrator',
'middleware' => ['web', 'auth'],
'authorization' => [
'enabled' => true,
'mode' => 'gate',
'gate' => 'viewTaskOrchestrator',
'user_field' => 'is_admin',
],
'database_connection' => env('TASK_ORCHESTRATOR_DB_CONNECTION'),
'discovery_path' => app_path('TaskOrchestrator/discovery.php'),
'fail_on_invalid_dependencies' => false,
'stale_run_default_minutes' => 10,
'health' => [
'queue_stuck_threshold_seconds' => 300,
'queue_worker' => [
'heartbeat_max_age_seconds' => 60,
],
'scheduler_heartbeat_max_age_seconds' => 180,
],
'notifications' => [
'enabled' => false,
'recipients' => [],
],
];
For dashboard access, define the configured gate:
use Illuminate\Support\Facades\Gate;
Gate::define('viewTaskOrchestrator', function ($user) {
return (bool) $user->is_admin;
});
For smaller applications you can also use mode => user_field and set user_field => is_admin.
Tasks are declared in the discovery file under the commands key. Each array key is an Artisan command signature and each value describes how Task Orchestrator should present and run it.
<?php
return [
'commands' => [
'import:users' => [
'name' => 'import-users',
'label' => 'Import users',
'description' => 'Imports users from the external source.',
'group' => 'Imports',
'group_order' => 10,
'order' => 10,
'connection' => 'database',
'queue' => 'imports',
'schedule' => [
'expression' => '*/15 * * * *',
'human' => 'Every 15 minutes',
],
'timeout_minutes' => 20,
],
],
];
Supported task metadata includes:
name: unique task identifier used by dependencieslabel: human-readable dashboard labeldescription: optional dashboard descriptiongroup, group_order, order: dashboard organizationdepends_on: task names that must succeed before this task runsconnection: queue connection for the task runqueue: queue name for the task runschedule.expression: cron expression for scheduled runsschedule.human: readable schedule text for the dashboardtimeout_minutes: per-task stale run timeoutnotifications: optional per-task notification settingsThis example keeps the commands generic while showing multiple dependencies:
<?php
return [
'commands' => [
'import:users' => [
'name' => 'import-users',
'label' => 'Import users',
'group' => 'Imports',
'order' => 10,
'queue' => 'imports',
'schedule' => [
'expression' => '0 * * * *',
'human' => 'Hourly',
],
'timeout_minutes' => 20,
],
'watch:users' => [
'name' => 'watch-users',
'label' => 'Watch imported users',
'group' => 'Imports',
'order' => 20,
'depends_on' => ['import-users'],
'queue' => 'imports',
'timeout_minutes' => 10,
],
'import:control-requirements' => [
'name' => 'import-control-requirements',
'label' => 'Import control requirements',
'group' => 'Reference Data',
'order' => 10,
'queue' => 'imports',
'timeout_minutes' => 30,
],
'import:resources' => [
'name' => 'import-resources',
'label' => 'Import resources',
'group' => 'Reference Data',
'order' => 20,
'depends_on' => ['import-control-requirements'],
'queue' => 'imports',
'timeout_minutes' => 30,
],
'import:services' => [
'name' => 'import-services',
'label' => 'Import services',
'group' => 'Reference Data',
'order' => 30,
'depends_on' => ['import-resources'],
'queue' => 'imports',
'timeout_minutes' => 30,
],
],
];
The resulting flows are:
import-users -> watch-users
import-control-requirements -> import-resources -> import-services
Task Orchestrator registers discovered task schedules with Laravel's scheduler. In local development you can run:
php artisan schedule:work
In production, run Laravel's scheduler every minute:
* * * * * cd /path/to/application && php artisan schedule:run >> /dev/null 2>&1
You also need at least one queue worker:
php artisan queue:work
If tasks use a custom queue such as imports, make sure a worker listens to it:
php artisan queue:work --queue=imports,default
The package also registers scheduler heartbeat, queue heartbeat and stale run recovery tasks through Laravel's scheduler.
After installation, open:
/task-orchestrator
The dashboard shows task groups, task status, recent runs, failed runs, pipeline execution, health status and links to run details. The route is protected by the configured middleware and the package authorization middleware.
Dependencies are defined with depends_on using task names:
'import:resources' => [
'name' => 'import-resources',
'depends_on' => ['import-control-requirements'],
],
When a task succeeds, Task Orchestrator starts downstream tasks whose dependencies are satisfied. Runs created from the same chain share a pipeline context, making it easier to inspect the full workflow.
Execution rules:
If a queued or running task hangs, the recovery command marks it as failed after its timeout:
php artisan task-orchestrator:recover-stale-runs
Timeout resolution:
timeout_minutesstale_run_default_minutes--minutes= overrideThe package schedules stale run recovery automatically. You can still run the command manually during incident response.
The dashboard reports:
healthy, busy or stuckrunning or downrunning or downQueue health is based on pending jobs and the configured stuck threshold. Scheduler health is based on a scheduler heartbeat written every minute. Queue worker health is based on a worker heartbeat refreshed by scheduled heartbeat jobs and task execution jobs.
When a scheduled task cannot be started (for example because queue worker heartbeat is stale), Task Orchestrator records a failed scheduled run with a clear failure reason in run history. The task is not retried immediately and will run again at the next regular cron due time.

Task detail screenshot placeholder. Add an image to docs/ and reference it here when available.
Health monitoring screenshot placeholder. Add an image to docs/ and reference it here when available.
Check authorization.mode, authorization.gate, authorization.user_field and the authenticated user. For gate mode, make sure the gate exists in your application.
Check that discovery_path points to an existing PHP file, the file returns an array and tasks are placed under the commands key.
Make sure Laravel's scheduler is running. Locally, use php artisan schedule:work. In production, configure cron to call php artisan schedule:run every minute.
Make sure a queue worker is running for the queue used by the task. If a task uses queue => imports, the worker must listen to imports.
Check that the scheduler is running every minute and that queue workers can process heartbeat jobs. Also verify that the application cache is available because heartbeat state is stored in cache.
Run php artisan task-orchestrator:recover-stale-runs and review timeout_minutes for long-running tasks.
Confirm that depends_on uses task names, not Artisan command names, and that upstream runs completed successfully.
More detailed documentation:
This package is early but usable. It is intended for Laravel applications that want a lightweight operational dashboard around Artisan command workflows without adopting a larger workflow engine.
Supported runtime versions:
Issues, bug reports, practical feedback and contributions are welcome through GitHub.
Run the package tests with PHPUnit:
vendor/bin/phpunit --configuration phpunit.xml --no-coverage
When developing this package as a local path repository inside a host application, you can also use the host application's PHPUnit binary:
../../vendor/bin/phpunit --configuration /absolute/path/to/packages/laravel-task-orchestrator/phpunit.xml --no-coverage
The package is open-sourced software licensed under the MIT license. The license declaration is available in composer.json.
How can I help you explore Laravel packages today?