Copied!
Programming
Laravel
PHP

Laravel Queue Complete Guide – How Queues Work & Why You Need Them

Laravel Queue Complete Guide – How Queues Work & Why You Need Them
Shahroz Javed
Mar 13, 2026 . 138 views

What is a Queue?

A queue is a line. Think of it like a restaurant order system — customers place orders (jobs), the kitchen processes them one by one in the background, and the customer doesn't have to wait standing at the counter.

In web development, a queue is a mechanism to defer time-consuming tasks — like sending emails, generating PDFs, resizing images, or calling external APIs — so they run in the background without slowing down the HTTP response for your user.

Laravel provides a unified queue API that works across multiple backends: database, Redis, Amazon SQS, and more — all with the same code.

Why Use Queues?

Every web request should respond as fast as possible. When your controller does heavy work synchronously, the user sits waiting. Here are common tasks that should never run during an HTTP request:

  • Sending emails — SMTP connections can take 1–3 seconds each

  • Sending SMS / push notifications — external API calls are slow and can fail

  • Image or video processing — resizing, compressing, transcoding

  • Generating reports / PDFs — heavy CPU work

  • Syncing data to third-party services — CRMs, analytics, ERPs

  • Sending bulk notifications — thousands of records need processing

By moving these tasks to a queue, your controller responds instantly and the heavy work happens asynchronously in the background.

⚠️ Rule of thumb: If a task takes more than 200ms or calls an external API, it belongs in a queue.

How Laravel Queues Work Internally

Understanding the internals makes everything else click. Here's the full flow:

  1. You dispatch a job — Laravel serializes the job class (with its data) and stores it in the queue backend (database row, Redis key, SQS message, etc.)

  2. The queue worker polls — a long-running PHP process (queue:work) continuously checks the queue for new jobs

  3. Worker picks up a job — it deserializes the job and calls its handle() method

  4. Job succeeds or fails — on success it's deleted from the queue; on failure it's retried or moved to the failed_jobs table

The key insight: your web server and your queue worker are two separate processes. Your web server (Nginx/Apache + PHP-FPM) handles HTTP requests. Your queue worker is a separate PHP process running in the background.

Supported Queue Drivers

Laravel supports multiple queue backends. You set the driver in your .env file:

QUEUE_CONNECTION=database

The available drivers and when to use each:

  • sync — runs jobs immediately, synchronously. No real queue. Used in local testing only.

  • database — stores jobs in a MySQL/PostgreSQL table. Great for small to medium apps. No extra server needed.

  • redis — stores jobs in Redis. Fast, production-ready. Supports Laravel Horizon for monitoring.

  • Amazon SQS — fully managed cloud queue. Perfect for AWS-hosted apps at scale.

  • beanstalkd — lightweight queue server. Less common today.

All configuration lives in config/queue.php. Each connection has a default queue name (usually default).

Create Your First Job

Generate a job class using Artisan:

php artisan make:job SendWelcomeEmail

This creates app/Jobs/SendWelcomeEmail.php. The generated class implements ShouldQueue, which tells Laravel to push it onto the queue instead of running it immediately.

<?php

namespace App\Jobs;

use App\Models\User;
use App\Mail\WelcomeEmail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        protected User $user
    ) {}

    public function handle(): void
    {
        Mail::to($this->user->email)->send(new WelcomeEmail($this->user));
    }
}

Understanding the Traits

  • Dispatchable — adds the static dispatch() method

  • InteractsWithQueue — lets the job release itself, delete itself, or check attempt count

  • Queueable — adds methods like onQueue() and onConnection()

  • SerializesModels — automatically serializes Eloquent models by their ID and re-fetches them when the job runs, preventing stale data

⚠️ Important: Because of SerializesModels, pass Eloquent model instances directly to the constructor — don't pass raw arrays of model data. Laravel will store just the ID and re-fetch a fresh copy when the job runs.

Dispatching Jobs

Once you have a job, you dispatch it from a controller, service, or anywhere in your app:

Basic Dispatch

use App\Jobs\SendWelcomeEmail;

// Dispatch to the default queue
SendWelcomeEmail::dispatch($user);

// Using the global helper
dispatch(new SendWelcomeEmail($user));

Delayed Dispatch

Delay a job so it doesn't run immediately:

// Run after 10 minutes
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(10));

// Run at a specific time
SendWelcomeEmail::dispatch($user)->delay(now()->addDay());

Conditional Dispatch

// Only dispatch if condition is true
SendWelcomeEmail::dispatchIf($user->email_verified, $user);

// Only dispatch if condition is false
SendWelcomeEmail::dispatchUnless($user->is_guest, $user);

Dispatch After Response

A lesser-known option: dispatch a job after the HTTP response is sent to the browser. The user gets their response instantly, and the job runs right after — no separate worker needed for quick tasks:

// Runs after the response is sent (still synchronous on the web server process)
SendWelcomeEmail::dispatchAfterResponse($user);
⚠️ dispatchAfterResponse() runs in the same PHP process as the request. It's not a real background job — don't use it for heavy tasks. Use a real queue worker for those.

Named Queues & Priorities

You can have multiple named queues and assign jobs to them. This lets you control priority — for example, process payment jobs before report generation jobs.

// Dispatch to specific named queues
SendWelcomeEmail::dispatch($user)->onQueue('emails');
ProcessCvUpload::dispatch($cv)->onQueue('cv');
GenerateReport::dispatch($report)->onQueue('reports');

When running the worker, list queues in priority order (left = highest priority):

php artisan queue:work --queue=payments,emails,cv,default

The worker will always exhaust the payments queue before touching emails, and so on.

Default Queue per Job Class

Set the default queue directly on the job class so you never forget to assign it:

class SendWelcomeEmail implements ShouldQueue
{
    public $queue = 'emails';
    // ...
}

Running the Queue Worker

The queue worker is a long-running PHP process that polls the queue and processes jobs. Start it with:

php artisan queue:work

Key Worker Options

# Process only one job then stop
php artisan queue:work --once

# Stop when the queue is empty
php artisan queue:work --stop-when-empty

# Set max retry attempts
php artisan queue:work --tries=3

# Set job timeout in seconds
php artisan queue:work --timeout=60

# Sleep X seconds when no jobs available (reduces CPU usage)
php artisan queue:work --sleep=3

# Stop after processing N jobs (prevents memory leaks)
php artisan queue:work --max-jobs=500

# Stop after running for N seconds
php artisan queue:work --max-time=3600

queue:work vs queue:listen

Many tutorials mix these up. Here's the difference:

  • queue:work — loads the framework once and keeps it in memory. Fast, but you must restart it after code changes.

  • queue:listen — spawns a fresh PHP process for every job. Slower, but picks up code changes automatically. Use only in development.

In production, always use queue:work and restart it after deployments:

# Signal all workers to gracefully restart after finishing current job
php artisan queue:restart

Conclusion

Laravel Queues are one of the most impactful features you can add to any production application. Moving slow tasks to the background makes your app feel instant and improves reliability.

  • Use php artisan make:job to create a job class

  • Implement ShouldQueue to mark it as a background job

  • Dispatch with JobClass::dispatch(), use ->delay() for deferred execution

  • Use named queues to control priority

  • Run queue:work in production (not queue:listen)

  • Always restart workers after deployments with queue:restart

📑 On This Page