Introduction to Cron Jobs in Laravel
In modern web applications, automation is essential. Whether it's sending daily newsletters, cleaning up expired sessions, generating reports, or syncing data with external APIs, repetitive tasks need to run without manual intervention.
Traditionally, developers rely on system-level cron jobs to execute PHP scripts at specific intervals. However, Laravel offers a much more elegant and developer-friendly solution: the Task Scheduler.
Instead of writing multiple cryptic cron entries on the server, Laravel lets you define all your scheduled tasks in PHP code within a single file. You only need one cron job on the server that calls php artisan schedule:run every minute, and Laravel handles the rest.
This approach is cleaner, version-controlled, testable, and integrates seamlessly with Laravel's ecosystem (queues, notifications, logging, etc.). In this comprehensive guide, we'll explore how Laravel cron jobs work, how to set them up, and walk through several practical real-world examples.
By the end, you'll be able to confidently automate any recurring task in your Laravel application.
What Are Cron Jobs?
A cron job is a time-based job scheduler in Unix-like operating systems. The name comes from chronos (Greek for time). It allows you to run commands or scripts automatically at specified intervals — every minute, hourly, daily, weekly, or at custom times.
The traditional crontab syntax looks like this:
* * * * * php /path/to/artisan some:command
Each asterisk represents minute, hour, day, month, and day-of-week. Managing many such lines becomes messy, especially when different commands need different schedules.
Laravel abstracts this complexity away with its fluent, readable API.
Laravel's Task Scheduler: How It Works
Laravel's scheduler is built on top of the Symfony Process component and integrates with the Artisan command system. All scheduling logic lives in app/Console/Kernel.php inside the schedule() method.
The only requirement on the server is a single cron entry:
* * * * * cd /path/to/your/project && php artisan schedule:run >> /dev/null 2>&1
Every minute, Laravel checks the defined schedule and runs any task whose frequency condition is met. If a task is not due, nothing happens — no overhead.
This design makes your scheduling logic part of your codebase, allowing version control, code reviews, and easy deployment.
Setting Up the Laravel Task Scheduler
Laravel comes with the scheduler out of the box (since version 5.3). No extra installation is required.
Step 1: Add the Server Cron Entry
Log into your server (usually via SSH) and run crontab -e to edit the crontab. Add this line (replace the path):
* * * * * cd /var/www/your-app && php artisan schedule:run >> /dev/null 2>&1
Step 2: Define Tasks in Kernel.php
Open app/Console/Kernel.php. You'll see the schedule() method:
protected function schedule(Schedule $schedule)
{
// Your scheduled tasks go here
}
Step 3: Testing Locally
To test without waiting, run:
php artisan schedule:work
This runs in the foreground and executes due tasks immediately, printing feedback.
Alternatively, use php artisan schedule:list to view all registered tasks and their next run times.
Defining Scheduled Tasks
You can schedule Artisan commands, closures, shell commands, or queued jobs.
Scheduling Artisan Commands
The most common approach:
$schedule->command('emails:send-daily-newsletter')
->daily()
->at('08:00')
->timezone('Asia/Dhaka');
Scheduling Closures
For quick one-off logic:
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
Scheduling Shell Commands
$schedule->exec('node /path/to/script.js')->weekly();
Scheduling Queued Jobs
$schedule->job(new ProcessPodcast)->hourly();
Available Frequency Options
Laravel provides a fluent chainable API with dozens of methods. Here are the most useful ones:
| Method | Description | Example |
|---|---|---|
->cron('* * * * *') | Custom cron expression | Every minute |
->everyMinute() | Every minute | |
->everyFiveMinutes() | Every 5 minutes | |
->hourly() | Top of every hour | |
->hourlyAt(15) | Every hour at 15 minutes past | |
->daily() | Midnight every day | |
->dailyAt('13:00') | Every day at 13:00 | |
->twiceDaily(1, 13) | At 1 AM and 1 PM | |
->weekly() | Sunday at 00:00 | |
->monthly() | First day of month at 00:00 | |
->weekdays() | Every weekday at 00:00 | |
->mondays()->at('08:30') | Every Monday at 08:30 |
You can chain constraints like ->when(), ->skip(), ->environments(), etc.
Real-World Examples of Laravel Cron Jobs
Example 1: Daily Email Newsletter
First, create an Artisan command:
php artisan make:command SendDailyNewsletter --command=emails:send-daily-newsletter
In the command's handle() method:
public function handle()
{
$users = User::where('subscribed', true)->get();
foreach ($users as $user) {
Mail::to($user)->send(new DailyNewsletter($user));
}
$this->info('Daily newsletter sent successfully!');
}
Schedule it:
$schedule->command('emails:send-daily-newsletter')
->dailyAt('07:00')
->timezone('Asia/Dhaka')
->withoutOverlapping()
->appendOutputTo(storage_path('logs/newsletter.log'));
Example 2: Clean Up Expired Password Resets
Laravel's default password_resets table accumulates entries. Clean old ones:
$schedule->call(function () {
DB::table('password_resets')
->where('created_at', '<', now()->subDays(7))
->delete();
})->dailyAt('02:00');
Example 3: Sync Exchange Rates from External API
Create a command:
php artisan make:command SyncExchangeRates
Inside handle():
$response = Http::get('https://api.exchangerate.host/latest?base=USD');
$rates = $response->json()['rates'];
foreach ($rates as $currency => $rate) {
CurrencyRate::updateOrCreate(
['currency' => $currency],
['rate' => $rate, 'updated_at' => now()]
);
}
Schedule:
$schedule->command('exchange:sync-rates')
->everySixHours()
->onSuccess(function () {
Log::info('Exchange rates synced successfully.');
});
Example 4: Generate Monthly Reports (Queued Job)
Create a queued job:
php artisan make:job GenerateMonthlyReport
Schedule the job directly:
$schedule->job(new GenerateMonthlyReport)
->monthlyOn(1, '09:00') // First day of month at 9 AM
->withoutOverlapping();
Example 5: Database Backup
Use Laravel's built-in backup (if using spatie/laravel-backup) or mysqldump:
$schedule->exec('mysqldump -u root -psecret database_name > /backups/db_'.date('Y-m-d').'.sql')
->dailyAt('03:00');
Advanced Scheduler Features
Preventing Overlaps
Use ->withoutOverlapping() to ensure a task doesn't start if a previous run is still active.
Running on Specific Environments
->environments(['production'])
Pinging URLs on Success/Failure
->pingBefore('https://healthchecks.io/ping/abc123/start')
->pingOnSuccess('https://healthchecks.io/ping/abc123')
->pingOnFailure('https://healthchecks.io/ping/abc123/fail')
Running in Maintenance Mode
Use ->evenInMaintenanceMode() for critical tasks.
Conditional Execution
->when(function () {
return Cache::get('reports_enabled', true);
})
Best Practices & Common Issues
- Use queues for long-running tasks to avoid timeout.
- Log output with
->appendOutputTo()or->emailOutputTo(). - Test thoroughly using
schedule:workandSchedule::assertCommandRan()in feature tests. - Handle timezones carefully with
->timezone(). - Monitor with tools like Healthchecks.io or Laravel Horizon.
Common Problems
- Cron not running → Check server crontab and path.
- Tasks not firing → Wrong timezone or server time mismatch.
- Permissions → Ensure the web user can write logs/backups.
- Shared hosting → Use Laravel's scheduler with external cron services like Easycron or Laravel Forge.
Conclusion
Laravel's Task Scheduler is one of the framework's most powerful yet underrated features. By moving scheduling logic into code, it makes automation reliable, maintainable, and enjoyable.
With just one server cron entry and expressive PHP syntax, you can handle everything from simple cleanups to complex workflows. The real-world examples above should give you a solid starting point for common use cases.
Start small — schedule a daily database cleanup today — and gradually automate more parts of your application. Your future self (and your servers) will thank you!
If you have questions or want to share your own scheduling patterns, drop a comment below.