Master Laravel Task Scheduling: From Cron Setup to Advanced Constraints
This guide explains how to replace per‑task Cron entries with Laravel's unified scheduler, showing how to configure a single Cron line, define schedules in app/Console/Kernel.php using closures, invokable objects, Artisan commands, queue jobs, shell commands, and how to fine‑tune frequency, time windows, environment, timezone, overlapping, single‑server execution, background processing, maintenance mode, output handling, hooks, and URL pings.
Introduction
Previously you might have created a separate Cron entry on the server for each scheduled task, which quickly becomes unwieldy because the schedules are not part of the source code and you must SSH into the server to add entries. Laravel's command scheduler lets you define all schedules inside the application, requiring only a single Cron entry on the server.
Starting the Scheduler
Add the following Cron line to your server (or use a service like Laravel Forge to manage it):
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1This runs the Laravel scheduler every minute; when schedule:run is executed, Laravel evaluates the tasks defined in app/Console/Kernel.php.
Defining Schedules
All scheduled tasks are defined in the schedule method of the App\Console\Kernel class. The example below schedules a daily closure that clears a table:
<?php
namespace App\Console;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected $commands = [];
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
}
}Invokable Objects
You can also schedule an invokable class instead of a closure:
$schedule->call(new DeleteRecentUsers)->daily();Artisan Command Scheduling
Schedule Artisan commands directly:
$schedule->command('emails:send --force')->daily();
$schedule->command(EmailsCommand::class, ['--force'])->daily();Queue Job Scheduling
Use the job method to dispatch a queued job without writing a closure:
$schedule->job(new Heartbeat)->everyFiveMinutes();
$schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes();Shell Command Scheduling
Run arbitrary shell commands with exec:
$schedule->exec('node /home/forge/script.js')->daily();Scheduling Frequency
Laravel provides many helper methods to set the execution interval: ->cron('* * * * *') – custom Cron expression
->everyMinute() ->everyFiveMinutes() ->hourly() ->daily() ->dailyAt('13:00') ->twiceDaily(1, 13) ->weekly() ->weeklyOn(1, '8:00') ->monthly() ->monthlyOn(4, '15:00') ->quarterly() ->yearly() ->timezone('America/New_York')Additional conditional helpers include ->weekdays(), ->weekends(), ->mondays(), ->between($start, $end), ->when(Closure), ->skip(Closure), and ->environments([...]).
Time Range Constraints
Limit execution to a specific time window with between or exclude a window with unlessBetween:
$schedule->command('reminders:send')
->hourly()
->between('7:00', '22:00');
$schedule->command('reminders:send')
->hourly()
->unlessBetween('23:00', '4:00');Conditional Execution
Run a task only when a closure returns true using when, or prevent execution with skip:
$schedule->command('emails:send')
->daily()
->when(function () { return true; });
$schedule->command('emails:send')
->daily()
->skip(function () { return true; });Environment Constraints
Execute a task only in specific environments:
$schedule->command('emails:send')
->daily()
->environments(['staging', 'production']);Timezone
Specify the timezone for a task:
$schedule->command('report:generate')
->timezone('America/New_York')
->at('02:00');To set a default timezone for all tasks, add a scheduleTimezone method to app/Console/Kernel.php:
protected function scheduleTimezone()
{
return 'America/Chicago';
}Be aware of daylight‑saving changes, which can cause tasks to run twice or be skipped.
Avoiding Overlap
Prevent a task from starting if a previous instance is still running:
$schedule->command('emails:send')->withoutOverlapping();You can set a custom lock expiration (in minutes):
$schedule->command('emails:send')->withoutOverlapping(10);Single‑Server Execution
When the application runs on multiple servers, ensure a task runs on only one server using onOneServer:
$schedule->command('report:generate')
->fridays()
->at('17:00')
->onOneServer();Background Tasks
Run long‑running commands in the background so subsequent tasks are not delayed:
$schedule->command('analytics:report')
->daily()
->runInBackground();Maintenance Mode
By default, scheduled queue jobs do not run in maintenance mode. To force execution, use evenInMaintenanceMode:
$schedule->command('emails:send')->evenInMaintenanceMode();Task Output
Direct task output to a file, append to a file, or email it:
$schedule->command('emails:send')
->daily()
->sendOutputTo($filePath);
$schedule->command('emails:send')
->daily()
->appendOutputTo($filePath);
$schedule->command('foo')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('[email protected]');Note: emailOutputTo, sendOutputTo, and appendOutputTo are only available for command and exec tasks.
Task Hooks
Execute custom code before or after a task with before and after:
$schedule->command('emails:send')
->daily()
->before(function () { /* task is about to start */ })
->after(function () { /* task is complete */ });Ping URLs
Notify external services by pinging a URL before or after a task using pingBefore and thenPing. Conditional variants pingBeforeIf and thenPingIf only ping when a given condition is true:
$schedule->command('emails:send')
->daily()
->pingBefore($url)
->thenPing($url);
$schedule->command('emails:send')
->daily()
->pingBeforeIf($condition, $url)
->thenPingIf($condition, $url);These methods require the Guzzle HTTP library. Install it via Composer:
composer require guzzlehttp/guzzleSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Laravel Tech Community
Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
