Claude
Skills
Sign in
Back

laravel-task-scheduling

Included with Lifetime
$97 forever

Best practices for Laravel task scheduling including defining schedules, frequency constraints, overlap prevention, and monitoring hooks.

Productivity

What this skill does


# Task Scheduling

## Defining Schedules in routes/console.php

```php
<?php

use Illuminate\Support\Facades\Schedule;

// Schedule an Artisan command
Schedule::command('reports:generate')->daily();

// Schedule a job
Schedule::job(new ProcessDailyMetrics)->dailyAt('01:00');

// Schedule a closure
Schedule::call(function () {
    Cache::flush();
})->weekly();

// Schedule a shell command
Schedule::exec('node /home/forge/script.js')->daily();
```

## Schedule Types

### Artisan Commands

```php
// With arguments and options
Schedule::command('emails:send', ['--force'])->daily();
Schedule::command('reports:generate --type=weekly')->sundays();

// Using command class
Schedule::command(SendEmailsCommand::class, ['--force'])->daily();
```

### Queued Jobs

```php
// Dispatch a job
Schedule::job(new CleanUpExpiredTokens)->daily();

// Dispatch to a specific queue and connection
Schedule::job(new ProcessAnalytics, 'analytics', 'redis')->hourly();
```

### Closures

```php
Schedule::call(function () {
    DB::table('sessions')
        ->where('last_activity', '<', now()->subHours(24))
        ->delete();
})->hourly()->description('Clean expired sessions');
```

### Shell Commands

```php
Schedule::exec('pg_dump mydb > /backups/db.sql')->daily();
```

## Frequency Methods

```php
// Common frequencies
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 each hour
Schedule::command('task')->everyOddHour();
Schedule::command('task')->everyTwoHours();
Schedule::command('task')->everyThreeHours();
Schedule::command('task')->everyFourHours();
Schedule::command('task')->everySixHours();
Schedule::command('task')->daily();
Schedule::command('task')->dailyAt('13:00');
Schedule::command('task')->twiceDaily(1, 13);       // At 1:00 and 13:00
Schedule::command('task')->twiceDailyAt(1, 13, 15); // At 1:15 and 13:15
Schedule::command('task')->weekly();
Schedule::command('task')->weeklyOn(1, '8:00');     // Monday at 8:00
Schedule::command('task')->monthly();
Schedule::command('task')->monthlyOn(4, '15:00');   // 4th of month at 15:00
Schedule::command('task')->twiceMonthly(1, 16);     // 1st and 16th
Schedule::command('task')->lastDayOfMonth('17:00');
Schedule::command('task')->quarterly();
Schedule::command('task')->quarterlyOn(4, '14:00'); // 4th day of quarter
Schedule::command('task')->yearly();
Schedule::command('task')->yearlyOn(6, 1, '17:00'); // June 1st at 17:00

// Cron expression
Schedule::command('task')->cron('0 */6 * * *');     // Every 6 hours
```

## Constraints

### Time Constraints

```php
// Between specific times
Schedule::command('task')->hourly()->between('8:00', '17:00');

// Outside specific times
Schedule::command('task')->hourly()->unlessBetween('23:00', '04:00');
```

### Day Constraints

```php
Schedule::command('task')->daily()->weekdays();
Schedule::command('task')->daily()->weekends();
Schedule::command('task')->daily()->sundays();
Schedule::command('task')->daily()->mondays();
Schedule::command('task')->daily()->tuesdays();
Schedule::command('task')->daily()->wednesdays();
Schedule::command('task')->daily()->thursdays();
Schedule::command('task')->daily()->fridays();
Schedule::command('task')->daily()->saturdays();
Schedule::command('task')->daily()->days([0, 3]);   // Sunday and Wednesday
```

### Environment Constraints

```php
// ✅ Only run in production
Schedule::command('analytics:process')->daily()->environments(['production']);

// ✅ Skip in testing
Schedule::command('reports:send')->daily()->environments(['production', 'staging']);
```

### Conditional Constraints

```php
// Run only when condition is true
Schedule::command('emails:send')
    ->daily()
    ->when(function () {
        return config('services.email.enabled');
    });

// Skip when condition is true
Schedule::command('maintenance:run')
    ->daily()
    ->skip(function () {
        return app()->isDownForMaintenance();
    });
```

## Overlap Prevention

```php
// ✅ Prevent overlapping: skip if previous instance is still running
Schedule::command('reports:generate')
    ->hourly()
    ->withoutOverlapping();

// With custom expiration (default is 24 hours)
Schedule::command('reports:generate')
    ->hourly()
    ->withoutOverlapping(expiresAt: 60); // Lock expires after 60 minutes
```

## Distributed Environments (onOneServer)

```php
// ✅ Only run on a single server (requires memcached, redis, or database cache driver)
Schedule::command('reports:generate')
    ->daily()
    ->onOneServer();

// Combine with overlap prevention
Schedule::command('analytics:process')
    ->hourly()
    ->onOneServer()
    ->withoutOverlapping();
```

## Schedule Groups

```php
// ✅ Apply shared configuration to multiple scheduled tasks
Schedule::command('analytics:aggregate')
    ->daily()
    ->onOneServer()
    ->withoutOverlapping()
    ->emailOutputOnFailure('[email protected]');

Schedule::command('analytics:cleanup')
    ->daily()
    ->onOneServer()
    ->withoutOverlapping()
    ->emailOutputOnFailure('[email protected]');

// Group with shared config (if using a helper)
collect([
    'analytics:aggregate',
    'analytics:cleanup',
    'analytics:summary',
])->each(function ($command) {
    Schedule::command($command)
        ->daily()
        ->onOneServer()
        ->withoutOverlapping()
        ->emailOutputOnFailure('[email protected]');
});
```

## Hooks (before, after, onSuccess, onFailure, ping)

```php
Schedule::command('reports:generate')
    ->daily()
    ->before(function () {
        Log::info('Starting report generation...');
    })
    ->after(function () {
        Log::info('Report generation complete.');
    })
    ->onSuccess(function () {
        Notification::route('slack', '#ops')
            ->notify(new ScheduledTaskSucceeded('reports:generate'));
    })
    ->onFailure(function () {
        Notification::route('slack', '#alerts')
            ->notify(new ScheduledTaskFailed('reports:generate'));
    });
```

### Pinging URLs (Health Checks)

```php
// Ping a URL before and after
Schedule::command('reports:generate')
    ->daily()
    ->pingBefore('https://health.example.com/start')
    ->thenPing('https://health.example.com/end');

// Ping only on success or failure
Schedule::command('reports:generate')
    ->daily()
    ->pingOnSuccess('https://health.example.com/success')
    ->pingOnFailure('https://health.example.com/failure');

// Works with services like Oh Dear, Healthchecks.io, Cronitor
Schedule::command('reports:generate')
    ->daily()
    ->pingBefore('https://hc-ping.com/your-uuid/start')
    ->thenPing('https://hc-ping.com/your-uuid');
```

### Email Output

```php
Schedule::command('reports:generate')
    ->daily()
    ->sendOutputTo('/var/log/reports.log')
    ->emailOutputTo('[email protected]');

// Only email on failure
Schedule::command('reports:generate')
    ->daily()
    ->emailOutputOnFailure('[email protected]');
```

## Sub-Minute Scheduling

```php
// ✅ Run every second (Laravel 11+)
Schedule::call(function () {
    // Process real-time data
    MetricsCollector::capture();
})->everySecond();

// Every 10 seconds
Schedule::call(function () {
    QueueMonitor::check();
})->everyTenSeconds();

// Every 15 seconds
Schedule::command('queue:monitor')->everyFifteenSeconds();

// Every 20 / 30 seconds
Schedule::call(fn () => HealthCheck::ping())->everyTwentySeconds();
Schedule::call(fn () => HealthCheck::ping())->everyThirtySeconds();
```

## Output Management

```php
// Send output to a file
Schedule::command('reports:generate')
    ->daily()
    ->sendOutputTo('/var/log/schedule/reports.log');

// Append output to a file
Schedule::command('reports:generate')
    ->daily()
    ->appendOutputTo('/var/log/sched

Related in Productivity