Laravel Queues and Jobs

Laravel queues move slow tasks, such as sending emails, resizing images or calling external APIs, out of the request so pages respond instantly. A job is pushed onto a queue, and a separate worker process runs it in the background.

Queue Configuration

Laravel 11 uses the database queue driver by default, and the jobs table is created by the default migrations. Redis or Amazon SQS are good choices for high traffic.

# .env
QUEUE_CONNECTION=database
(Jobs are stored in the jobs table until a worker processes them)

Creating a Job

Models passed to the constructor are serialized by ID and reloaded fresh when the job runs.

php artisan make:job SendWelcomeEmail

<?php
namespace App\Jobs;

use App\Mail\WelcomeMail;
use App\Models\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $backoff = 60;

    public function __construct(public User $user) {}

    public function handle(): void
    {
        Mail::to($this->user)->send(new WelcomeMail($this->user));
    }
}
INFO  Job [app/Jobs/SendWelcomeEmail.php] created successfully.

Dispatching Jobs

<?php
SendWelcomeEmail::dispatch($user);
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(10));
GenerateReport::dispatch($month)->onQueue('reports');

// Queue a mailable directly
Mail::to($user)->queue(new WelcomeMail($user));
(Request finishes immediately; jobs wait in the queue)

Running the Queue Worker

Workers are long-running processes. In production, keep them alive with Supervisor or use Laravel Horizon for Redis queues. Restart workers after deploying new code.

php artisan queue:work --queue=default,reports --tries=3
php artisan queue:restart
INFO  Processing jobs from the [default,reports] queues.
2026-10-31 10:15:02 App\Jobs\SendWelcomeEmail ........ RUNNING
2026-10-31 10:15:03 App\Jobs\SendWelcomeEmail ........ 812.40ms DONE

Handling Failed Jobs

When a job throws an exception on its last attempt, it is stored in failed_jobs. Add a failed() method to react, and retry from the command line.

php artisan queue:failed
php artisan queue:retry all
php artisan queue:flush
+------+------------+---------+----------------------------+---------------------+
| ID   | Connection | Queue   | Class                      | Failed At           |
| 9f1e | database   | default | App\Jobs\SendWelcomeEmail  | 2026-10-31 10:20:11 |
+------+------------+---------+----------------------------+---------------------+
INFO  Pushing failed queue jobs back onto the queue.

Scheduling Tasks

For recurring work, schedule commands or jobs in routes/console.php and run php artisan schedule:run every minute via cron.

<?php
use Illuminate\Support\Facades\Schedule;

Schedule::job(new PruneOldLogs)->daily();
Schedule::command('reports:send')->weekdays()->at('08:00');
(PruneOldLogs runs daily at midnight; reports:send at 08:00 Mon-Fri)

Frequently asked questions

Why is my Laravel job not running?

A worker must be running: start php artisan queue:work. Also check QUEUE_CONNECTION is not sync and that the job implements ShouldQueue.

What is the sync queue driver?

It runs jobs immediately inside the request, which is handy for testing but gives no performance benefit.

Do I need to restart queue workers after deploying?

Yes. Workers keep the old code in memory, so run php artisan queue:restart as part of every deployment.