This tutorial will guide you through setting up and utilizing Laravel Task Scheduler, covering task creation, scheduling frequencies, preventing overlaps, running tasks on one server, and practical examples to streamline your application's automated tasks.
Laravel Task Scheduler is a powerful feature of the Laravel framework, enabling developers to schedule time-based tasks with ease. This system simplifies automating tasks like sending emails, cleaning databases, and generating reports without the need for creating complicated Cron jobs. With Laravel Task Scheduler, you can define tasks directly within your application's code, specify execution intervals ranging from every minute to yearly, and manage task overlaps and server execution, enhancing your application's efficiency and reliability.
How Laravel Task Scheduler Works?
Here is a step by step guide to understand how Laravel Task Scheduler will schedule any provided task in the provided order:
- Cron Job Initialization: A single Cron job is set up on the server to execute
php artisan schedule:run
command every minute. This is the heart of Laravel's scheduler, enabling it to check and run tasks as needed. - Defining Tasks in Laravel: Developers define their scheduled tasks within the
schedule
method of theapp/Console/Kernel.php
file. Here, you can specify various tasks, such as running Artisan commands, executing custom PHP functions, or even queuing jobs and their execution frequency. - Scheduling Frequency: Laravel offers a wide array of scheduling frequencies (
hourly
,daily
,weekly
, etc.). When you specify a frequency for a task, Laravel calculates the next execution time based on this schedule. - Execution Time Check: Every minute, when the Cron job runs the
schedule:run
command, Laravel checks the current time against the next scheduled execution times of all defined tasks. - Task Execution: If the current time matches or surpasses a task's scheduled execution time, Laravel runs the task. Once completed, it calculates and stores the next execution time for the task.
- Preventing Overlaps: If a task is marked with
withoutOverlapping()
, Laravel ensures that the task does not run again if it's already in progress. This is managed by setting a lock in the application cache, which is checked before the task is run. - Server-Specific Execution: For applications running in a load-balanced environment,
onOneServer()
ensures that a scheduled task runs on only one server. This relies on Laravel's cache system to set a server-wide lock. - Hooks for Additional Logic:
before()
andafter()
hooks allow for the execution of custom logic before and after a scheduled task runs, providing flexibility for complex scheduling needs. - Monitoring and Management: Developers can monitor and manage scheduled tasks using commands like
php artisan schedule:list
(available in Laravel 8.x and above) to view all scheduled tasks and their next execution times. - Manual Task Execution: For testing or manual execution,
php artisan schedule:run
can be run from the command line. This will execute any tasks that are due at the current time, based on their defined schedules.
php artisan schedule:run
command is called by the Cron job every minute Laravel is designed to manage task execution based on the schedules as provided in app/Console/Kernel.php
. Laravel does this by keeping track of the last execution time of each scheduled task internally, ensuring that tasks run at their specified frequencies and not more often than they should.
Steps to implement and validate Laravel Task Scheduler with Example
1. Setup Environment
If you already have an environment to setup your scheduler then you can skip this section. I am documenting these steps for anyone who are starting fresh and are planning to learn this feature:
I already have a setup with Laravel installed. So I will directly proceed and create a new project laravelSchedulerProject
to demonstrate the functioning of Laravel Task Scheduler.
composer create-project --prefer-dist laravel/laravel laravelSchedulerProject
Navigate inside the project:
cd laravelSchedulerProject/
I will update .env
file with the database details which I have already created when I had installed Laravel:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=my_laravel_db DB_USERNAME=user1 DB_PASSWORD=Passw0rd
I will generate a migration for the users
table and include a verified
boolean column:

In the generated migration file located in database/migrations/2024_02_19_072928_create_users_table.php
, add:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->boolean('verified')->default(false);
$table->timestamps();
});
Run the migration:
php artisan migrate

Create a User model if it doesn't already exist:
php artisan make:model User
2. Create a Custom Command for Cleanup
Generate a new Artisan command for cleaning up unverified users:
php artisan make:command CleanUpUnverifiedUsers

Edit the generated app/Console/Commands/CleanUpUnverifiedUsers.php
file. In the handle()
method, add logic to delete unverified users who were created more than 48 hours ago:
<?php namespace App\Console\Commands; use Illuminate\Console\Command; class CleanUpUnverifiedUsers extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'cleanup:unverifiedusers'; /** * The console command description. * * @var string */ protected $description = 'Cleans up unverified users'; /** * Execute the console command. */ public function handle() { $count = \App\Models\User::where('verified', false) ->where('created_at', '<', now()->subDays(2)) ->delete(); $this->info("$count unverified users cleaned up."); } }
protected $signature
: This property defines the name and signature of the console command. It's how we will call the command from the terminal. In our case, the signature is'cleanup:unverifiedusers'
. So, we would run this command via the terminal asphp artisan cleanup:unverifiedusers
.protected $description
: This property provides a description for the console command. This description is displayed when we runphp artisan list
or when we request help for the command usingphp artisan help cleanup:unverifiedusers
. It's meant to give users a quick overview of what the command does.public function handle()
: Thehandle()
method contains the logic that is executed when the command is called. This is where we define what the command actually does. When we run the command, Laravel automatically calls this method.
3. Schedule the Cleanup Command
In app/Console/Kernel.php
, schedule the cleanup command to run daily:
<?php namespace App\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; class Kernel extends ConsoleKernel { /** * Define the application's command schedule. */ protected function schedule(Schedule $schedule): void { // $schedule->command('inspire')->hourly(); $schedule->command('cleanup:unverifiedusers')->everyMinute(); } /** * Register the commands for the application. */ protected function commands(): void { $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } }
$signature
in the custom Artisan command class and the command string we pass to the $schedule->command()
method in the schedule function of app/Console/Kernel.php
should be the same.For testing purpose as I want to test this scheduler immediately so I have configured it to run every minute but you can modify it as per your requirement.
Laravel provides a wide range of scheduling options to run tasks at various intervals:
- Every Minute: Use
->everyMinute();
for tasks that should run as frequently as possible. - Hourly: Use
->hourly();
to run tasks once every hour. - Daily: The
->daily();
method schedules tasks to run once a day at midnight. - Monthly:
->monthlyOn(1, '15:00');
schedules a task to run on the first day of every month at 3 PM.
4. Create Dummy Users (Optional)
To test the scheduler, first, create some dummy unverified users with a mix of timestamps. Use Tinker to insert these users:
\App\Models\User::insert([
['name' => 'John Doe', 'email' => 'john@example.com', 'verified' => false, 'created_at' => now()->subDays(3), 'updated_at' => now()->subDays(3)],
['name' => 'Jane Doe', 'email' => 'jane@example.com', 'verified' => false, 'created_at' => now()->subHour(), 'updated_at' => now()->subHour()]
]);

5. Testing Scheduled Tasks
Manually trigger the scheduler to see the cleanup command in action:
php artisan schedule:run

Check your database to see if the unverified user created more than 48 hours ago has been removed.
To test a specific scheduled task, you might want to run it immediately without waiting for its scheduled time. You can manually trigger this command for testing purposes with:
php artisan cleanup:unverifiedusers

6. Set Up Cron Job
For local manual tests, running php artisan schedule:run
directly is fine, but for actual automated scheduling, ensure your system's Cron is set up to call this command every minute:
crontab -e
Next add the following rule in your crontab. You can modify the rule based on your project name.
* * * * * cd /opt/projects/laravelSchedulerProject && php artisan schedule:run >> /dev/null 2>&1
Â
7. Listing Scheduled Tasks
As of Laravel 8.x, a useful command schedule:list
is available to list all scheduled tasks configured in your application. This command gives you a quick overview of what tasks are scheduled and their frequency.
php artisan schedule:list

Additional Options supported with Laravel Task Scheduler
1. Preventing Task Overlaps
In Laravel's Task Scheduler, preventing task overlaps is crucial for long-running tasks to avoid multiple instances of the same task running simultaneously. This can be especially important for tasks that may take longer to complete than their scheduled frequency.
When you chain the withoutOverlapping()
method to a task's definition in app/Console/Kernel.php
, Laravel ensures that the task will not run if it is already running. This is achieved by setting a lock in the cache that lasts for the duration of the task's execution. If the next scheduled run of the task occurs before the previous run has completed, Laravel will skip the new run.
$schedule->command('cleanup:unverifiedusers')->daily()->withoutOverlapping();
2. Running Tasks on One Server
In environments where your Laravel application is deployed across multiple servers behind a load balancer, ensuring that scheduled tasks run on only one server is necessary to prevent duplicate executions.
This feature requires the use of Laravel's cache driver that supports atomic locks, such as Redis or Memcached. By chaining the onOneServer()
method to your task's scheduling, Laravel uses the cache to set a lock that is respected across all servers. This ensures that only the first server to acquire the lock can run the task, while other servers will skip the execution.
$schedule->command('cleanup:unverifiedusers')->daily()->onOneServer();
3. Using Task Hooks: Before and After
Task hooks allow you to execute custom code before and after a scheduled task runs. This is useful for preparing the application for a task or cleaning up after a task has completed.
before(Closure $callback)
: Executes the provided callback before the scheduled task runs. This can be used for logging, sending notifications, or any preparatory actions.after(Closure $callback)
: Executes the provided callback after the scheduled task has run. This is useful for cleanup operations, logging, or post-task notifications.
$schedule->command('cleanup:unverifiedusers')->daily()
->before(function () {
Log::info('Starting cleanup of unverified users.');
})
->after(function () {
Log::info('Cleanup of unverified users complete.');
});
4. Running Tasks in the Background
For long-running tasks, you may want to execute them in the background to prevent blocking other tasks or the scheduler itself. Laravel allows you to run scheduled tasks in the background.
To run a command in the background, use the runInBackground()
method:
$schedule->command('send:reminders')->hourly()->runInBackground();
This is particularly useful for tasks that take a significant amount of time to complete, ensuring they don't interfere with the execution of other scheduled tasks.
5. Capture Scheduler Task's Output and Errors to Log File
For more comprehensive monitoring, especially in production, you might need to log the output of scheduled tasks or even monitor their execution and failures. You can log the output of a task to a file by chaining the sendOutputTo()
method.
$schedule->command('database:backup')->daily()->sendOutputTo(storage_path('logs/database_backup.log'));
This method is useful for debugging and monitoring the task's output without manually checking the system each time.
To log both the output and any errors generated by a scheduled task, you can use the sendOutputTo()
method for the output and appendOutputTo()
for appending both standard output and error output to the same file or handle errors more explicitly.
$schedule->command('cleanup:unverifiedusers')
->daily()
->sendOutputTo(storage_path('logs/cleanup_run.log'))
->appendOutputTo(storage_path('logs/cleanup_run_error.log'));
This setup directs the command output to cleanup_run.log
and appends error output to clanup_run_error.log
.
However, if you want both standard output and errors in the same file, you can direct stderr
to stdout
in the command definition itself, assuming your command might generate error output that is different from Laravel's application log:
$schedule->exec('cleanup:unverifiedusers 2>&1')
->daily()
->sendOutputTo(storage_path('logs/cleanup_run_combined.log'));
Here, 2>&1
redirects the standard error (stderr
, file descriptor 2) to the standard output (stdout
, file descriptor 1), ensuring that all output, including errors, is captured in the same log file.
Frequently Asked Questions on Laravel Scheduling
Why is my scheduled task not running?
The most common reason a scheduled task isn't running is due to the server's Cron job not being correctly set up. Laravel's scheduler relies on a Cron job that executes php artisan schedule:run
every minute. If this isn't configured, or there's an error in your Cron setup, Laravel won't be able to trigger any scheduled tasks. Additionally, ensure your tasks are correctly defined in app/Console/Kernel.php
with the correct syntax and scheduling expressions. The php artisan schedule:list
can be used to check for any configuration or PHP parse errors. Environmental configuration differences or discrepancies in the expected environment (like differences in .env
file settings) can also affect task execution.
How can I debug a scheduled task?
To debug a scheduled task, you can manually list schuled task using php artisan schedule:list
or try to run the task using the Artisan command line tool with its specific signature php artisan cleanup:unverifiedusers
, which allows you to see any immediate errors or output. You can also use the ->sendOutputTo()
method to log the task's output to a file for later inspection. Checking Laravel's log files and ensuring your task is correctly defined in app/Console/Kernel.php
are also key steps in debugging scheduled tasks.
My task runs manually but not when scheduled. What could be wrong?
If your task runs manually but not when scheduled, check the task's schedule frequency and timing in app/Console/Kernel.php
. Tasks scheduled for specific times won't run if php artisan schedule:run
is executed at a different time. Ensure your server's timezone matches the application's timezone configuration in config/app.php
. Also, review the server's Cron job setup to ensure it's running php artisan schedule:run
every minute as required.
How do I prevent task overlaps?
To prevent task overlaps, use the ->withoutOverlapping()
method when defining your task in app/Console/Kernel.php
. This method prevents a new instance of the task from starting if the previous instance is still running, by leveraging Laravel's cache system to set a lock.
Can I schedule tasks to run at random times?
Laravel does not support scheduling tasks at completely random times directly. However, you can achieve a degree of randomness by scheduling a task to run more frequently (e.g., every minute) and incorporating conditional logic within the task to randomly determine whether to proceed with the task's execution.
How do I ensure a task runs only on one server in a load-balanced setup?
To ensure a task runs on only one server in a load-balanced environment, use the ->onOneServer()
method when scheduling your task. This method requires your application to use a cache driver that supports atomic locks, such as Redis or Memcached, to maintain a lock ensuring that only one server can run the task at a time.
What happens if a scheduled task fails?
If a scheduled task fails, Laravel does not automatically retry the task. It's recommended to log any failures, either by capturing the output of the task using ->sendOutputTo()
or by implementing error handling within the task itself to log exceptions or errors to Laravel's log files. For critical tasks, consider manually implementing retry logic or leveraging Laravel's queue system for automatic retries with built-in failure handling.
Summary
Laravel's Task Scheduler enables efficient automation of repetitive tasks through a simple Cron job setup that triggers php artisan schedule:run
every minute. It's essential to ensure correct Cron configuration, accurate task scheduling in app/Console/Kernel.php
, and alignment of environment settings for tasks to run as expected. Debugging involves manual execution, logging outputs, and checking configurations. Preventing overlaps, ensuring single-server execution in load-balanced setups, and managing random execution timings require specific method usages like withoutOverlapping()
, onOneServer()
, and conditional logic. Task failures don't automatically retry, highlighting the importance of logging and custom error handling strategies for maintaining robust task scheduling.