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');
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.