Install the Package
composer require bobrd/scheduler-bundle
Add to config/app.php under providers:
BobrD\SchedulerBundle\SchedulerBundle::class,
Publish Config (Optional)
php artisan vendor:publish --provider="BobrD\SchedulerBundle\SchedulerBundle" --tag="config"
Default config is minimal; check config/scheduler.php for tweaks.
Define a Job
Create a command (e.g., php artisan make:command ProcessQueueJob).
Annotate with @Schedule in the class or method:
use BobrD\SchedulerBundle\Annotation\Schedule;
class ProcessQueueJob extends Command
{
/**
* @Schedule("0 * * * *") // Runs every minute
*/
public function handle()
{
// Job logic
}
}
Run the Scheduler
Add to app/Console/Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('process_queue_job')->everyMinute();
}
Start the scheduler in artisan schedule:run (or use a cron job):
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1
@Schedule or using Schedule::command().$schedule->call(function () {
// One-time logic
})->at('03:00');
$schedule->command('backup_db')->hourlyAt(config('scheduler.backup_time'));
$schedule->command('send_notifications')->daily()->withoutOverlapping();
$schedule->command('generate_reports')->onOneServer()->then(function () {
event(new ReportsGenerated());
});
before()/after() hooks:
$schedule->command('cleanup_logs')
->daily()
->before(function () {
Log::info('Starting cleanup...');
});
$schedule->command('sync_data')->timezone('America/New_York')->hourly();
$schedule->command('fetch_feeds')->random(minute: 15);
$schedule->command('process_invoices')->when(function () {
return Invoice::pending()->exists();
});
Cron Syntax Confusion
Schedule facade (not raw cron). Avoid mixing * wildcards with Laravel’s helpers (e.g., everyMinute() vs. "* * * * *").->everyMinute()) unless you need cron-specific features.Missing schedule:run
artisan schedule:run. Test locally with:
* * * * * cd /path && php artisan schedule:run >> /dev/null 2>&1
laravel-scheduler package for production monitoring.Overlapping Jobs
withoutOverlapping(), jobs may pile up. Enable it for critical tasks:
$schedule->command('long_running_task')->withoutOverlapping();
Environment-Specific Schedules
Kernel.php run in all environments. Use when() or environment checks:
if (app()->environment('production')) {
$schedule->command('monitor_health')->everyFiveMinutes();
}
schedule:run to a log file:
* * * * * cd /path && php artisan schedule:run >> /var/log/laravel_scheduler.log 2>&1
php artisan schedule:list
php artisan your:command
Custom Schedulers
Override the default scheduler by binding your own Illuminate\Contracts\Schedule\Schedule in the container.
Annotation Parser
Extend the @Schedule annotation logic by modifying the bundle’s AnnotationReader service.
Event Dispatching
Listen for scheduling:running events to log or modify schedules dynamically:
Event::listen('scheduling:running', function ($command) {
Log::debug("Running scheduled command: {$command}");
});
laravel-horizon: For queue-based scheduling, pair with Horizon for job monitoring.spatie/laravel-schedule).ping command to verify the scheduler is running:
$schedule->command('ping')->everyFiveMinutes();
How can I help you explore Laravel packages today?