Stop Polling. Start Scaling: How to Run Laravel Scheduled Tasks + Queued Jobs Like a Pro

Laravel’s built-in task scheduler is one of its most underrated superpowers. Combine it with queues, and you unlock scalable, non-blocking automation—perfect for sending emails, syncing APIs, cleaning up data, or generating reports.

Here’s how to do it right.


🧭 Why Use Queues with Scheduled Tasks?

By default, Laravel’s scheduler runs tasks synchronously. That means if a task takes 30 seconds, it blocks the scheduler for 30 seconds. Not ideal.

Using queues lets you:

  • Run tasks asynchronously in the background
  • Avoid blocking the scheduler
  • Retry failed jobs automatically
  • Monitor and scale with tools like Horizon

🛠️ Step 1: Create a Scheduled Command

Use Artisan to generate a custom command:

php artisan make:command SyncExternalUsers

Inside app/Console/Commands/SyncExternalUsers.php, define your logic in the handle() method. But instead of doing the work directly, dispatch a queued job:

public function handle()
{
    SyncUsersJob::dispatch();
}

📦 Step 2: Create the Queued Job

Generate the job:

php artisan make:job SyncUsersJob

Inside handle(), write your actual sync logic:

public function handle()
{
    $users = Http::get('https://api.example.com/users');
    foreach ($users as $user) {
        User::updateOrCreate([...]);
    }
}

This job will run in the background, keeping your scheduler fast and responsive.


⏰ Step 3: Schedule the Command

Open app/Console/Kernel.php and schedule your command:

protected function schedule(Schedule $schedule)
{
    $schedule->command('sync:external-users')->hourly();
}

You can use any frequency: daily(), everyMinute(), weeklyOn(), etc.


🧩 Step 4: Set Up the Cron Job

On your server, set up a single cron entry to run Laravel’s scheduler:

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

This runs every minute and checks if any scheduled tasks need to be triggered.


🔄 Real-Time CDC with Laravel Model Events + Queues

Want to react to data changes instantly—without polling or cron? Laravel’s model events let you implement lightweight CDC (Change Data Capture) using queues.

🧠 What You’re Capturing

  • Created → new records added
  • Updated → fields modified
  • Deleted → records removed

🛠️ Setup: Dispatch Jobs on Model Events

class User extends Model
{
    protected static function booted()
    {
        static::created(fn($user) => SyncUserToCRM::dispatch($user));
        static::updated(fn($user) => SyncUserToCRM::dispatch($user));
        static::deleted(fn($user) => RemoveUserFromCRM::dispatch($user));
    }
}

Each event triggers a queued job—no blocking, no polling.

🔐 Bonus: Filter Meaningful Changes

Only dispatch jobs when relevant fields change:

static::updated(function ($user) {
    if ($user->isDirty(['email', 'name'])) {
        SyncUserToCRM::dispatch($user);
    }
});

✅ Use Cases

  • Syncing users to CRMs or marketing platforms
  • Updating search indexes (Algolia, Meilisearch)
  • Triggering webhooks or notifications
  • Auditing and logging changes
  • Real-time dashboards or analytics

This gives you real-time CDC behavior using Laravel’s native tools—no polling, no cron, no external dependencies.


🚀 Bonus: Use Horizon for Monitoring

If you’re using Redis as your queue driver, install Laravel Horizon for real-time monitoring, retry management, and performance insights.

composer require laravel/horizon
php artisan horizon:install

✅ Final Thoughts

Laravel’s scheduler + queue + model events combo is a clean, scalable way to automate tasks and react to data changes without slowing down your app. Whether you’re building a SaaS, e-commerce platform, or internal tool, this pattern keeps your logic modular, testable, and production-ready.

Leave a Reply

Your email address will not be published. Required fields are marked *