Back to Blog
Lesson 33 of the Intermediate Laravel: Real-World Application Patterns course
LaravelJune 26, 20263 min read

Scheduled Tasks and Cron Jobs: Automating Laravel Workflows

Learn to master Laravel scheduling to automate recurring tasks like reporting and cleanup. Replace complex crontabs with clean, version-controlled PHP code.

LaravelSchedulingAutomationCronDevOpsArtisanphpbackend

Previously in this course, we explored Command Line Tools with Artisan: Automating Laravel Workflows. Now that you can build custom CLI commands, the next logical step is automating their execution. Instead of managing dozens of individual server-level cron jobs, Laravel allows you to define your entire schedule within your application code.

The Problem with Traditional Cron

In a standard Linux environment, you would edit your crontab file to run commands. This approach is brittle: your schedule lives outside your repository, it's hard to version control, and it's a nightmare to manage across staging and production environments.

Laravel’s scheduling system acts as a wrapper around the system cron. You only need to add a single entry to your server's crontab, and Laravel handles the rest.

Defining Scheduled Tasks

In modern Laravel versions, the schedule is defined in routes/console.php. You use the Schedule facade to define tasks that should run at specific intervals.

Let’s advance our project board application. We want to automatically delete tasks that have been marked as "archived" for more than 30 days.

First, ensure you have an Artisan command (as learned in our previous lesson) called app:prune-old-tasks. Now, register it in routes/console.php:

PHP
use Illuminate\Support\Facades\Schedule;

Schedule::command('app:prune-old-tasks')->daily();

The daily() method is a helper for a standard cron expression. You can also use everyMinute(), weekly(), or monthly().

Advanced Scheduling with Cron Expressions

When built-in helpers aren't enough, you can use the cron() method to pass raw cron expressions. This gives you full control over the timing.

For example, if you need to run a report every Monday at 3:00 AM:

PHP
Schedule::command('app:generate-weekly-report')
    ->cron('0 3 * * 1');

You can also chain methods to control execution constraints:

PHP
Schedule::command('app:prune-old-tasks')
    ->dailyAt('02:00')
    ->environments(['production'])
    ->withoutOverlapping();
  • environments(['production']): Prevents the task from running in local or staging environments.
  • withoutOverlapping(): Ensures that if the previous execution is still running, the new one won't start. This is critical for long-running cleanup jobs.

Handling Task Output

When tasks run in the background, you won't see their output in your terminal. To debug or keep a record, you should direct the output to a log file or send it via email.

PHP
Schedule::command('app:prune-old-tasks')
    ->daily()
    ->appendOutputTo(storage_path('logs/scheduler.log'))
    ->emailOutputOnFailure('admin@example.com');

Using appendOutputTo is essential for production monitoring. If a task fails silently, you need that log file to diagnose the issue.

Hands-on Exercise

  1. Open your project's routes/console.php file.
  2. Register a new task that runs app:generate-weekly-report every Sunday at midnight.
  3. Add withoutOverlapping() to ensure it doesn't collide with other processes.
  4. Append the output to storage/logs/reports.log.
  5. Verify your schedule by running php artisan schedule:list in your terminal.

Common Pitfalls

  • Forgetting the single Cron entry: You must add * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 to your server’s crontab. Without this, your defined schedule will never trigger.
  • Overlapping Tasks: If a task takes longer than the interval (e.g., a report that takes 2 minutes to generate but is scheduled everyMinute), you will end up with multiple processes competing for database locks. Always use withoutOverlapping().
  • Environment Leaks: Running cleanup commands in your local environment can be destructive. Always use ->environments(['production']) for sensitive tasks.

Recap

Scheduling in Laravel turns complex server management into readable, version-controlled code. By using the Schedule facade, we can define our task intervals, handle overlaps, and ensure our application maintains its health through automated cleanup and reporting. This is a core component of building robust, autonomous systems.

Up next: We will dive into Job Chaining and Batching, where we'll learn how to handle complex, multi-step background processes that depend on each other.

Similar Posts