| name | laravel-task-scheduling |
| description | Best practices for Laravel task scheduling including defining schedules, frequency constraints, overlap prevention, and monitoring hooks. |
Task Scheduling
Defining Schedules in routes/console.php
<?php
use Illuminate\Support\Facades\Schedule;
Schedule::command('reports:generate')->daily();
Schedule::job(new ProcessDailyMetrics)->dailyAt('01:00');
Schedule::call(function () {
Cache::flush();
})->weekly();
Schedule::exec('node /home/forge/script.js')->daily();
Schedule Types
Artisan Commands
Schedule::command('emails:send', ['--force'])->daily();
Schedule::command('reports:generate --type=weekly')->sundays();
Schedule::command(SendEmailsCommand::class, ['--force'])->daily();
Queued Jobs
Schedule::job(new CleanUpExpiredTokens)->daily();
Schedule::job(new ProcessAnalytics, 'analytics', 'redis')->hourly();
Closures
Schedule::call(function () {
DB::table('sessions')
->where('last_activity', '<', now()->subHours(24))
->delete();
})->hourly()->description('Clean expired sessions');
Shell Commands
Schedule::exec('pg_dump mydb > /backups/db.sql')->daily();
Frequency Methods
Schedule::command('task')->everyMinute();
Schedule::command('task')->everyTwoMinutes();
Schedule::command('task')->everyFiveMinutes();
Schedule::command('task')->everyTenMinutes();
Schedule::command('task')->everyFifteenMinutes();
Schedule::command('task')->everyThirtyMinutes();
Schedule::command('task')->hourly();
Schedule::command('task')->hourlyAt(17);
Schedule::command('task')->everyOddHour();
Schedule::command('task')->everyTwoHours();
Schedule::command('task')->everyThreeHours();
Schedule::command('task')->everyFourHours();
Schedule::command('task')->everySixHours();
Schedule::command('task')->daily();
Schedule::command('task')->dailyAt('13:00');
Schedule::command('task')->twiceDaily(1, 13);
Schedule::command('task')->twiceDailyAt(1, 13, 15);
Schedule::command('task')->weekly();
Schedule::command('task')->weeklyOn(1, '8:00');
Schedule::command('task')->monthly();
Schedule::command('task')->monthlyOn(4, '15:00');
Schedule::command('task')->twiceMonthly(1, 16);
Schedule::command('task')->lastDayOfMonth('17:00');
Schedule::command('task')->quarterly();
Schedule::command('task')->quarterlyOn(4, '14:00');
Schedule::command('task')->yearly();
Schedule::command('task')->yearlyOn(6, 1, '17:00');
Schedule::command('task')->cron('0 */6 * * *');
Constraints
Time Constraints
Schedule::command('task')->hourly()->between('8:00', '17:00');
Schedule::command('task')->hourly()->unlessBetween('23:00', '04:00');
Day Constraints
Schedule::command('task')->daily()->weekdays();
Schedule::command('task')->daily()->weekends();
Schedule::command('task')->daily()->sundays();
Schedule::command('task')->daily()->mondays();
Schedule::command('task')->daily()->tuesdays();
Schedule::command('task')->daily()->wednesdays();
Schedule::command('task')->daily()->thursdays();
Schedule::command('task')->daily()->fridays();
Schedule::command('task')->daily()->saturdays();
Schedule::command('task')->daily()->days([0, 3]);
Environment Constraints
Schedule::command('analytics:process')->daily()->environments(['production']);
Schedule::command('reports:send')->daily()->environments(['production', 'staging']);
Conditional Constraints
Schedule::command('emails:send')
->daily()
->when(function () {
return config('services.email.enabled');
});
Schedule::command('maintenance:run')
->daily()
->skip(function () {
return app()->isDownForMaintenance();
});
Overlap Prevention
Schedule::command('reports:generate')
->hourly()
->withoutOverlapping();
Schedule::command('reports:generate')
->hourly()
->withoutOverlapping(expiresAt: 60);
Distributed Environments (onOneServer)
Schedule::command('reports:generate')
->daily()
->onOneServer();
Schedule::command('analytics:process')
->hourly()
->onOneServer()
->withoutOverlapping();
Schedule Groups
Schedule::command('analytics:aggregate')
->daily()
->onOneServer()
->withoutOverlapping()
->emailOutputOnFailure('admin@example.com');
Schedule::command('analytics:cleanup')
->daily()
->onOneServer()
->withoutOverlapping()
->emailOutputOnFailure('admin@example.com');
collect([
'analytics:aggregate',
'analytics:cleanup',
'analytics:summary',
])->each(function ($command) {
Schedule::command($command)
->daily()
->onOneServer()
->withoutOverlapping()
->emailOutputOnFailure('admin@example.com');
});
Hooks (before, after, onSuccess, onFailure, ping)
Schedule::command('reports:generate')
->daily()
->before(function () {
Log::info('Starting report generation...');
})
->after(function () {
Log::info('Report generation complete.');
})
->onSuccess(function () {
Notification::route('slack', '#ops')
->notify(new ScheduledTaskSucceeded('reports:generate'));
})
->onFailure(function () {
Notification::route('slack', '#alerts')
->notify(new ScheduledTaskFailed('reports:generate'));
});
Pinging URLs (Health Checks)
Schedule::command('reports:generate')
->daily()
->pingBefore('https://health.example.com/start')
->thenPing('https://health.example.com/end');
Schedule::command('reports:generate')
->daily()
->pingOnSuccess('https://health.example.com/success')
->pingOnFailure('https://health.example.com/failure');
Schedule::command('reports:generate')
->daily()
->pingBefore('https://hc-ping.com/your-uuid/start')
->thenPing('https://hc-ping.com/your-uuid');
Email Output
Schedule::command('reports:generate')
->daily()
->sendOutputTo('/var/log/reports.log')
->emailOutputTo('admin@example.com');
Schedule::command('reports:generate')
->daily()
->emailOutputOnFailure('admin@example.com');
Sub-Minute Scheduling
Schedule::call(function () {
MetricsCollector::capture();
})->everySecond();
Schedule::call(function () {
QueueMonitor::check();
})->everyTenSeconds();
Schedule::command('queue:monitor')->everyFifteenSeconds();
Schedule::call(fn () => HealthCheck::ping())->everyTwentySeconds();
Schedule::call(fn () => HealthCheck::ping())->everyThirtySeconds();
Output Management
Schedule::command('reports:generate')
->daily()
->sendOutputTo('/var/log/schedule/reports.log');
Schedule::command('reports:generate')
->daily()
->appendOutputTo('/var/log/schedule/reports.log');
Schedule::command('reports:generate')
->daily()
->appendOutputTo(storage_path('logs/reports.log'));
Schedule::command('reports:generate')
->daily()
->sendOutputTo('/dev/null');
Running in Maintenance Mode
Schedule::command('queue:work --stop-when-empty')
->everyMinute()
->evenInMaintenanceMode();
Running the Scheduler
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1
php artisan schedule:work
php artisan schedule:list
php artisan schedule:test
Best Practices
Schedule::call(function () {
})->daily()->description('Clean expired sessions');
Schedule::command('sitemap:generate')->daily()->onOneServer();
Schedule::command('import:products')->hourly()->withoutOverlapping();
Schedule::command('billing:charge')
->daily()
->onSuccess(fn () => Log::info('Billing completed'))
->onFailure(fn () => Log::critical('Billing failed'))
->pingOnFailure('https://alerts.example.com/billing');
Schedule::command('heavy:task')->everyMinute();
Schedule::command('report:daily')
->dailyAt('09:00')
->timezone('America/New_York');
Checklist