Laravel Framework

Task Scheduling

15 min Lesson 19 of 45

Task Scheduling in Laravel

Laravel's task scheduler provides a fluent, expressive API for defining scheduled tasks within your application. With the scheduler, you only need a single cron entry on your server, and your scheduled tasks are defined in code, making them version-controlled and easier to manage.

Setting Up the Scheduler

Add a single cron entry to your server that runs Laravel's scheduler every minute:

# Edit crontab crontab -e # Add this line * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 # For production, log output * * * * * cd /path-to-your-project && php artisan schedule:run >> /var/log/laravel-schedule.log 2>&1
Note: The scheduler runs every minute and checks which tasks are due to run. This single cron entry is all you need - Laravel handles the rest internally.

Defining Schedules

Define scheduled tasks in the schedule method of app/Console/Kernel.php:

<?php namespace App\Console; use App\Jobs\GenerateMonthlyReport; use App\Jobs\ProcessSubscriptions; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Illuminate\Support\Facades\DB; class Kernel extends ConsoleKernel { protected function schedule(Schedule $schedule) { // Run command every minute $schedule->command('emails:send') ->everyMinute(); // Run command hourly $schedule->command('reports:generate') ->hourly(); // Run command daily at specific time $schedule->command('users:cleanup') ->dailyAt('02:00'); // Run job $schedule->job(new ProcessSubscriptions) ->daily(); // Run closure $schedule->call(function () { DB::table('recent_activity')->delete(); })->daily(); // Execute shell command $schedule->exec('node /home/forge/script.js') ->daily(); // Queue a command $schedule->command('emails:send') ->everyFiveMinutes() ->onOneServer() ->runInBackground(); } protected function commands() { $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } }

Schedule Frequency Options

Laravel provides many frequency options for scheduling tasks:

// Time-based frequencies $schedule->command('command')->everyMinute(); // Every minute $schedule->command('command')->everyTwoMinutes(); // Every 2 minutes $schedule->command('command')->everyFiveMinutes(); // Every 5 minutes $schedule->command('command')->everyTenMinutes(); // Every 10 minutes $schedule->command('command')->everyFifteenMinutes(); // Every 15 minutes $schedule->command('command')->everyThirtyMinutes(); // Every 30 minutes // Hourly schedules $schedule->command('command')->hourly(); // Every hour $schedule->command('command')->hourlyAt(17); // Every hour at 17 minutes past $schedule->command('command')->everyTwoHours(); // Every 2 hours $schedule->command('command')->everyThreeHours(); // Every 3 hours $schedule->command('command')->everyFourHours(); // Every 4 hours $schedule->command('command')->everySixHours(); // Every 6 hours // Daily schedules $schedule->command('command')->daily(); // Daily at midnight $schedule->command('command')->dailyAt('13:00'); // Daily at 1:00 PM $schedule->command('command')->twiceDaily(1, 13); // Daily at 1:00 AM and 1:00 PM $schedule->command('command')->twiceDailyAt(1, 13, 15); // Daily at 1:15 AM and 1:15 PM // Weekly schedules $schedule->command('command')->weekly(); // Weekly on Sunday at midnight $schedule->command('command')->weeklyOn(1, '8:00'); // Weekly on Monday at 8:00 AM // Monthly schedules $schedule->command('command')->monthly(); // Monthly on the 1st at midnight $schedule->command('command')->monthlyOn(4, '15:00'); // Monthly on the 4th at 3:00 PM $schedule->command('command')->twiceMonthly(1, 16); // Monthly on the 1st and 16th at midnight $schedule->command('command')->lastDayOfMonth('15:00'); // Last day of month at 3:00 PM // Quarterly and yearly $schedule->command('command')->quarterly(); // Quarterly on the 1st day at midnight $schedule->command('command')->yearly(); // Yearly on Jan 1st at midnight $schedule->command('command')->yearlyOn(6, 1, '17:00'); // Yearly on June 1st at 5:00 PM // Weekday schedules $schedule->command('command')->weekdays(); // Monday through Friday at midnight $schedule->command('command')->weekends(); // Saturday and Sunday at midnight // Specific days $schedule->command('command')->mondays(); // Every Monday at midnight $schedule->command('command')->tuesdays(); // Every Tuesday at midnight $schedule->command('command')->wednesdays(); // Every Wednesday at midnight $schedule->command('command')->thursdays(); // Every Thursday at midnight $schedule->command('command')->fridays(); // Every Friday at midnight $schedule->command('command')->saturdays(); // Every Saturday at midnight $schedule->command('command')->sundays(); // Every Sunday at midnight

Conditional Scheduling

Run tasks based on conditions using when and skip methods:

// Run when condition is true $schedule->command('emails:send') ->daily() ->when(function () { return date('w') != 0; // Don't run on Sundays }); // Skip when condition is true $schedule->command('emails:send') ->daily() ->skip(function () { return DB::table('holidays') ->whereDate('date', today()) ->exists(); }); // Between time ranges $schedule->command('command') ->hourly() ->between('8:00', '17:00'); // Outside time ranges $schedule->command('command') ->hourly() ->unlessBetween('23:00', '4:00');

Task Output and Logging

Control where task output is sent:

// Send output to file $schedule->command('emails:send') ->daily() ->sendOutputTo('/var/log/emails.log'); // Append output to file $schedule->command('emails:send') ->daily() ->appendOutputTo('/var/log/emails.log'); // Email output $schedule->command('report:generate') ->daily() ->emailOutputTo('admin@example.com'); // Email output only on failure $schedule->command('report:generate') ->daily() ->emailOutputOnFailure('admin@example.com'); // Ping URL before/after task $schedule->command('emails:send') ->daily() ->pingBefore('https://example.com/before-hook') ->pingAfter('https://example.com/after-hook'); // Ping on success/failure $schedule->command('emails:send') ->daily() ->pingBeforeIf($condition, 'https://example.com/before') ->thenPing('https://example.com/success') ->pingOnFailure('https://example.com/failure');
Tip: Use services like Healthchecks.io or Cronitor to monitor scheduled tasks with the ping methods.

Preventing Task Overlaps

Prevent tasks from overlapping if previous execution is still running:

// Prevent overlap using mutex $schedule->command('emails:send') ->everyFiveMinutes() ->withoutOverlapping(); // Custom overlap expiration (in minutes) $schedule->command('emails:send') ->everyFiveMinutes() ->withoutOverlapping(10); // Expire lock after 10 minutes // Skip if still running $schedule->command('process:data') ->hourly() ->withoutOverlapping() ->skip(function () { return $this->isStillRunning(); });

Running Tasks on Single Server

When running multiple servers, ensure tasks run on only one server:

// Requires cache configuration $schedule->command('report:generate') ->daily() ->onOneServer(); // With custom mutex name $schedule->command('report:generate') ->daily() ->onOneServer('report-generation-lock');
Note: The onOneServer() method requires a cache driver that supports locks (Redis, Memcached, DynamoDB, or database).

Background Tasks

Run scheduled tasks in the background for better performance:

// Run in background $schedule->command('emails:send') ->everyFiveMinutes() ->runInBackground(); // Multiple background tasks can run simultaneously $schedule->command('analytics:generate') ->hourly() ->runInBackground(); $schedule->command('reports:generate') ->hourly() ->runInBackground();

Maintenance Mode

Control task behavior during maintenance mode:

// Run even in maintenance mode $schedule->command('critical:task') ->hourly() ->evenInMaintenanceMode(); // Skip during maintenance mode (default) $schedule->command('non-critical:task') ->hourly();

Before and After Hooks

Execute code before and after scheduled tasks:

$schedule->command('emails:send') ->daily() ->before(function () { // Task is about to execute \Log::info('Starting email send task'); }) ->after(function () { // Task has executed \Log::info('Email send task completed'); }); // On success callback $schedule->command('emails:send') ->daily() ->onSuccess(function () { \Log::info('Emails sent successfully'); }); // On failure callback $schedule->command('emails:send') ->daily() ->onFailure(function () { \Log::error('Email send task failed'); // Send alert to admin });

Testing Scheduled Tasks

Test scheduled tasks manually or in automated tests:

// List all scheduled tasks php artisan schedule:list // Run scheduled tasks manually php artisan schedule:run // Test specific task php artisan schedule:test --name="emails:send" // View next due date for tasks php artisan schedule:list // In tests use Illuminate\Support\Facades\Schedule; public function test_command_is_scheduled() { $this->artisan('schedule:list') ->expectsOutput('emails:send'); }
Exercise 1: Create a scheduled task that generates daily sales reports at 6:00 AM, sends them via email to managers, and archives reports older than 90 days. Implement proper error handling and notifications.
Exercise 2: Build a subscription reminder system that sends reminders 7 days, 3 days, and 1 day before subscription expiration. Use conditional scheduling to skip holidays and weekends.
Exercise 3: Create a database backup scheduler that runs hourly during business hours (8 AM - 6 PM), uploads backups to S3, and deletes local backups older than 24 hours. Implement health check pings and failure notifications.

Common Scheduling Patterns

// Database cleanup $schedule->command('db:cleanup') ->daily() ->at('03:00') ->onOneServer() ->withoutOverlapping(); // Cache warming $schedule->call(function () { Cache::remember('popular_products', 3600, function () { return Product::popular()->get(); }); })->hourly(); // Sitemap generation $schedule->command('sitemap:generate') ->weekly() ->sundays() ->at('01:00') ->runInBackground(); // Send digest emails $schedule->job(new SendWeeklyDigest) ->weekly() ->fridays() ->at('09:00'); // Monitor application health $schedule->call(function () { Http::post('https://beats.envoyer.io/heartbeat/...', [ 'status' => 'up', ]); })->everyFiveMinutes();

Task scheduling is a powerful feature for automating recurring tasks in your Laravel applications. In the next lesson, we'll explore caching strategies for improving application performance.