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.
