Laravel 8 Cron/Task Scheduling

Laravel 8 gives you lot of flexibilities in terms of cron/task scheduling. You can easily manage all of your background tasks in one place. You can even configure them in nice human readable code.

Run schedular in background

In order for laravel to manage all of your cron/task you need to add following command to your cronjob entry. If you are using linux or mac run following command to run laravel schedular in background.

# open your crontab entry
crontab -e

# choose your editor of choice and add
# following line to your crontab file
* * * * * /path-to-your-project/php artisan schedule:run >> /dev/null 2>&1

What above command would do is that it will run this schedular in the background every minute. Now, next step is to setup our laravel schedular and add our background tasks in there so that laravel can process them.

All cron/tasks are manage in App\Console\Kernel class. Let's open our class file to see how it looks like:

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\DB;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // All background tasks go here
    }
}

You would have to define all of your background tasks in schedule function. Followings are different methods you can use in order to schedule your task in schedule function.

<?php

namespace App\Console;

use Illuminate\Support\Facades\DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

use App\Jobs\Heartbeat;
use App\Console\Commands\SendEmailsCommand;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // run email send command everyday
        $schedule->command('emails:send --force')->daily(); // OR
        $schedule->command(SendEmailsCommand::class, ['--force'])->daily();

        // schedule laravel job to run every five minutes
        $schedule->job(new Heartbeat)->everyFiveMinutes();

        // Dispatch the job to the "heartbeats" queue on the "sqs" connection...
        $schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();

        // you can even execute bash command to run it daily
        $schedule->exec('node /home/forge/script.js')->daily();

        // run specific class on daily basis
        // following class must have __invoke method
        // which will be called when this job runs
        $schedule->call(new DeleteRecentUsers)->daily();

        // run following command at specified timezone
        $schedule->command('report:generate')
                 ->timezone('America/New_York')
                 ->at('2:00');

        // run following command in specific env
        $schedule->command('emails:send')
                    ->daily()
                    ->environments(['staging', 'production']);

        // schedule a command to run hourly on Sundays and Wednesdays
        $schedule->command('emails:send')
                        ->hourly()
                        ->days([0, 3]);

        // run command in specific time window
        $schedule->command('emails:send')
                    ->hourly()
                    ->between('7:00', '22:00');

        // By default, scheduled tasks will be run even if the previous instance 
        // of the task is still running. To prevent this, you may use the withoutOverlapping method
        $schedule->command('emails:send')->withoutOverlapping();

        // if you have multiple servers running you can tell command
        // to run on only single server to prevent duplication
        $schedule->command('report:generate')
                        ->fridays()
                        ->at('17:00')
                        ->onOneServer();

        // multiple tasks scheduled at the same time will execute sequentially 
        // based on the order they are defined in your schedule method. 
        // If you have long-running tasks, this may cause subsequent tasks to start 
        // much later than anticipated. If you would like to run tasks in the background so that 
        // they may all run simultaneously, you may use the runInBackground method
        $schedule->command('analytics:report')
                 ->daily()
                 ->runInBackground();

        // force task to run even in maintainance mode
        $schedule->command('emails:send')->evenInMaintenanceMode();

        // you can send your command output to 
        // specified file path so that you can view the log later
        $schedule->command('emails:send')
                 ->daily()
                 ->sendOutputTo($filePath);

        // append command output to previous log file rather then override
        $schedule->command('emails:send')
                 ->daily()
                 ->appendOutputTo($filePath);

        // send email alert when task fails
        $schedule->command('report:generate')
                 ->daily()
                 ->emailOutputOnFailure('taylor@example.com');

        // ping certain url when task is done or has error
        $schedule->command('emails:send')
                 ->daily()
                 ->pingOnSuccess($successUrl)
                 ->pingOnFailure($failureUrl);
    }
}

Laravel Schedular Frequency Options

You can chain methods to meet your need as shown below. Use Frequency table to find appropriate methods.

// Run hourly from 8 AM to 5 PM on weekdays...
$schedule->command('foo')
          ->weekdays()
          ->hourly()
          ->timezone('America/Chicago')
          ->between('8:00', '17:00');
Method Description
->cron('* * * * *'); Run the task on a custom cron schedule
->everyMinute(); Run the task every minute
->everyTwoMinutes(); Run the task every two minutes
->everyThreeMinutes(); Run the task every three minutes
->everyFourMinutes(); Run the task every four minutes
->everyFiveMinutes(); Run the task every five minutes
->everyTenMinutes(); Run the task every ten minutes
->everyFifteenMinutes(); Run the task every fifteen minutes
->everyThirtyMinutes(); Run the task every thirty minutes
->hourly(); Run the task every hour
->hourlyAt(17); Run the task every hour at 17 minutes past the hour
->everyTwoHours(); Run the task every two hours
->everyThreeHours(); Run the task every three hours
->everyFourHours(); Run the task every four hours
->everySixHours(); Run the task every six hours
->daily(); Run the task every day at midnight
->dailyAt('13:00'); Run the task every day at 13:00
->twiceDaily(1, 13); Run the task daily at 1:00 & 13:00
->weekly(); Run the task every Sunday at 00:00
->weeklyOn(1, '8:00'); Run the task every week on Monday at 8:00
->monthly(); Run the task on the first day of every month at 00:00
->monthlyOn(4, '15:00'); Run the task every month on the 4th at 15:00
->twiceMonthly(1, 16, '13:00'); Run the task monthly on the 1st and 16th at 13:00
->lastDayOfMonth('15:00'); Run the task on the last day of the month at 15:00
->quarterly(); Run the task on the first day of every quarter at 00:00
->yearly(); Run the task on the first day of every year at 00:00
->yearlyOn(6, 1, '17:00'); Run the task every year on June 1st at 17:00
->timezone('America/New_York'); Set the timezone for the task