Home › Cron Jobs with Laravel

Cron Jobs in Laravel

Laravel Task Scheduling replaces individual crontab entries with a single entry that drives all your scheduled jobs from PHP code.

The One Crontab Entry

Laravel's entire scheduling system runs from a single cron entry. Add this to your server's crontab and never touch it again. Official reference: Laravel Task Scheduling documentation.

* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1

This runs every minute. Laravel decides internally which jobs are due. All your schedule definitions live in PHP code, version-controlled with your project.

The cd part matters

The cd /var/www/html && sets the working directory before running Artisan. Without it, Artisan cannot find your .env file or bootstrap Laravel correctly. Replace /var/www/html with the actual path to your Laravel project root.

Defining Schedules (Laravel 9+)

In Laravel 9 and above, schedules go in routes/console.php:

use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily();
Schedule::command('reports:generate')->weeklyOn(1, '8:00');
Schedule::command('cache:clear')->hourly();
Schedule::call(function () {
    DB::table('temp_data')->where('created_at', '<', now()->subDays(7))->delete();
})->daily()->at('03:00');

Frequency Methods

MethodDescriptionEquivalent cron
->everyMinute()Every minute* * * * *
->everyFiveMinutes()Every 5 minutes*/5 * * * *
->hourly()Every hour0 * * * *
->daily()Every day at midnight0 0 * * *
->dailyAt('13:00')Every day at 1 PM0 13 * * *
->weekly()Every Sunday at midnight0 0 * * 0
->weeklyOn(1, '8:00')Every Monday at 8 AM0 8 * * 1
->monthly()First of every month0 0 1 * *
->cron('30 9 * * 1-5')Custom expressionAny expression

Important Options

Prevent overlapping runs

By default, Laravel runs a job even if the previous one is still running. For long jobs, add withoutOverlapping():

Schedule::command('import:data')
    ->hourly()
    ->withoutOverlapping();

Run only on one server

If you have multiple app servers, each one runs the scheduler. Use onOneServer() to ensure only one fires the job (requires a cache driver that supports atomic locks):

Schedule::command('report:generate')
    ->daily()
    ->onOneServer();

Timezone

By default, schedules use the app timezone from config/app.php. Override per job:

Schedule::command('emails:send')
    ->daily()
    ->timezone('Europe/Madrid');

Output to log

Send job output to a file or email:

Schedule::command('backup:run')
    ->daily()
    ->appendOutputTo('/var/log/backup.log');

Debugging Laravel Schedules

List all scheduled jobs

php artisan schedule:list

Shows every scheduled job, its expression and when it runs next.

Run the scheduler manually

php artisan schedule:run

Triggers any jobs due to run right now. Useful for testing without waiting for cron.

Run a specific job immediately

php artisan schedule:test

Interactive command that lets you pick a scheduled job and run it immediately, regardless of its schedule.

Keep the scheduler running locally

php artisan schedule:work

Runs the scheduler every minute in the foreground - useful for local development without a crontab.

Need a custom cron expression for ->cron()?

Use the generator to build your expression visually, then paste it directly into the ->cron('...') method.