Command output — line(), info(), error(), table(), progressBar()
Beginner5 min read·lv-25-003
Concept
Scheduling runs Artisan commands (and PHP callables) automatically at defined intervals. Laravel's scheduler replaces N cron entries with a single cron job that runs php artisan schedule:run every minute.
Single cron entry:
cron
* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1This fires every minute. The scheduler decides which tasks are due and runs them.
Defining schedules: In routes/console.php (Laravel 11+) or app/Console/Kernel.php:
Frequency methods (chainable):
->everyMinute(),->everyFiveMinutes(),->everyFifteenMinutes(),->everyThirtyMinutes().->hourly(),->hourlyAt(17)— at minute 17.->daily(),->dailyAt('13:00'),->twiceDaily(1, 13).->weekly(),->weeklyOn(1, '8:00')— Monday at 8am.->monthly(),->monthlyOn(4, '15:00')— 4th of month at 3pm.->cron('0 */6 * * *')— raw cron expression.
Constraints:
->weekdays()/->weekends(): Limit to weekdays or weekends.->between('8:00', '17:00'): Only within time range.->when(callable): Custom condition.->environments('production'): Only on specific environments.
Safety:
->withoutOverlapping(): Don't run if previous instance still running (uses a cache lock).->onOneServer(): Only one server runs the task (requires cache + same Redis/DB).
schedule:work: For development — polls every minute without a cron job.
Code Example
php
<?php
// routes/console.php (Laravel 11+)
use Illuminate\Support\Facades\Schedule;
// Run artisan commands
Schedule::command('report:weekly')->weekly()->mondays()->at('8:00');
Schedule::command('orders:process')->everyFifteenMinutes()->withoutOverlapping();
Schedule::command('backup:run')->dailyAt('2:00')->environments('production');
Schedule::command('cache:prune-stale-tags')->hourly();
Schedule::command('telescope:prune --hours=48')->daily();
// Call a closure (for lightweight tasks)
Schedule::call(function() {
\App\Models\User::where('last_login_at', '<', now()->subYear())->update(['active' => false]);
})->monthly()->at('1:00');
// Call an invokable class
Schedule::call(new \App\Jobs\CleanTempFilesJob)->daily();
// Queue a job
Schedule::job(new \App\Jobs\ProcessPendingOrders)->everyFiveMinutes()->onQueue('scheduled');
// Run shell command
Schedule::exec('node /path/to/script.js')->daily();
// onOneServer — only run on ONE server (requires shared Redis/DB cache)
Schedule::command('newsletter:send')->daily()->at('9:00')->onOneServer();
// Safety constraints
Schedule::command('payment:reconcile')
->dailyAt('23:00')
->withoutOverlapping(10) // max 10 minute overlap window
->onOneServer()
->environments(['production', 'staging'])
->when(fn() => config('features.payments_enabled'));
// app/Console/Kernel.php (Laravel 10 and below)
// protected function schedule(Schedule $schedule): void
// {
// $schedule->command('report:weekly')->weekly();
// }