Introduction to Task Scheduling

+15 Mana ✨

Introduction

Laravel's scheduler lets you define cron-like recurring tasks in PHP, not in /etc/crontab. You write one system cron entry that calls schedule:run every minute, and from then on every recurring task lives in routes/console.php with readable fluent methods like ->daily() and ->everyFiveMinutes().

Key Concepts

  • Scheduler: The machinery that runs every minute and executes any tasks due in that minute.
  • schedule:run: The artisan command triggered by the system cron — it consults the scheduler and runs anything due.
  • Frequency modifier: Methods like ->daily(), ->hourly(), ->everyFifteenMinutes() that control how often a task runs.
  • Constraint: Methods like ->weekdays(), ->between(), ->when() that further restrict when a task runs.

Real World Context

Every app has "daily at 2am" work: clean up old sessions, send digest emails, run backups, prune soft-deleted rows. Managing that via crontab means a dozen system-level entries that must be kept in sync with the code. Laravel's scheduler collapses all of it into one file in the repo.

Deep Dive

Why Use the Scheduler?

Traditional Cron:                    Laravel Scheduler:
┌─────────────────────────────────┐  ┌─────────────────────────────────┐
│ # Server crontab (complex!)     │  │ # Server crontab (simple!)      │
│ 0 * * * * /path/to/report.php   │  │ * * * * * cd /app && artisan   │
│ */5 * * * * /path/to/cleanup.php│  │           schedule:run >> /log │
│ 0 0 * * * /path/to/backup.php   │  └─────────────────────────────────┘
│ 30 2 * * 0 /path/to/prune.php   │
│ ...many more entries...         │  All scheduling defined in PHP!
└─────────────────────────────────┘

Defining Schedules

Define schedules in routes/console.php:

php
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily();
Schedule::command('reports:generate')->weeklyOn(1, '8:00');
Schedule::command('cache:clear')->hourly();

Scheduling Artisan Commands

php
// Run an Artisan command
Schedule::command('emails:send --force')->daily();

// With arguments
Schedule::command('user:cleanup', ['--days' => 30])->weekly();

// Signature style
Schedule::command('inspire')->hourly();

Scheduling Queued Jobs

php
use App\Jobs\ProcessReports;
use App\Jobs\CleanupDatabase;

Schedule::job(new ProcessReports)->daily();
Schedule::job(new CleanupDatabase, 'maintenance')->weekly();

Scheduling Shell Commands

php
Schedule::exec('node /home/user/scripts/backup.js')->daily();
Schedule::exec('mysqldump database > backup.sql')->dailyAt('02:00');

Scheduling Closures

php
Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();

Schedule::call(fn () => cache()->flush())->weekly();

Schedule Frequency Options

php
// Time-based
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);  // At :17 past
Schedule::command('task')->everyTwoHours();
Schedule::command('task')->daily();
Schedule::command('task')->dailyAt('13:00');
Schedule::command('task')->twiceDaily(1, 13);  // 1am and 1pm
Schedule::command('task')->weekly();
Schedule::command('task')->weeklyOn(1, '8:00');  // Monday 8am
Schedule::command('task')->monthly();
Schedule::command('task')->monthlyOn(4, '15:00');  // 4th at 3pm
Schedule::command('task')->quarterly();
Schedule::command('task')->yearly();
Schedule::command('task')->yearlyOn(6, 1, '17:00');  // June 1st 5pm

// Custom cron
Schedule::command('task')->cron('0 * * * *');

Timezone

php
Schedule::command('report:generate')
    ->timezone('America/New_York')
    ->dailyAt('09:00');

Running the Scheduler

Add one cron entry to your server:

bash
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

For local development:

bash
php artisan schedule:work  # Runs scheduler every minute

Common Pitfalls

  1. Forgetting the system cron entry — the PHP scheduler only does anything when schedule:run is called. Without the * * * * * crontab line, your whole schedule is dead code.
  2. Using wall-clock frequencies without a timezone — ->dailyAt('09:00') runs at 9am in the server's PHP timezone, which may not match your users. Chain ->timezone('America/New_York') to pin it.

Best Practices

  1. Keep all schedule definitions in routes/console.php — one file to audit, one place to change.
  2. Prefer ->dailyAt('HH:MM') over raw ->cron('...') — the fluent form reads better in diff and can't have a typo in the five-field cron string.

Summary

  • Define recurring tasks in routes/console.php with Schedule::command(), Schedule::job(), or Schedule::call().
  • One schedule:run cron entry per minute drives the entire schedule.
  • Frequency methods (->daily(), ->hourly(), etc.) combine with constraints (->weekdays(), ->between()).
  • schedule:work runs the scheduler locally for development.
✓ Completed