Laravel Queues Explained

Laravel Queues Explained

Cover
Boris Lukrece
18 May 2026 2 views 4 min read

Laravel Queues Explained: How to Handle Background Jobs the Right Way

If your app does anything that takes more than a second — sending emails, processing files, calling external APIs — you should not be doing it in the request cycle. Laravel’s queue system is the solution, and it’s more approachable than most developers think.

Background processing and server jobs

Why Queues Matter

Without queues, every slow operation blocks the user:

User clicks "Send Invoice"
→ App calls PDF generator (2s)
→ App calls email service (1s)
→ App waits for response (1s)
→ User finally sees "Done" after 4 seconds

With queues:

User clicks "Send Invoice"
→ App dispatches a job (5ms)
→ User sees "Done" immediately
→ Queue worker processes PDF + email in background

Same result. Completely different user experience.

Setting Up Laravel Queues

1. Configure your queue driver

In your .env file:

QUEUE_CONNECTION=database

For production, use redis — it’s faster and more reliable than the database driver. For local development, database is fine.

2. Create the jobs table (if using database driver)

php artisan queue:table
php artisan migrate

3. Create your first job

php artisan make:job SendInvoiceEmail

This generates app/Jobs/SendInvoiceEmail.php:

<?php

namespace App\Jobs;

use App\Models\Invoice;
use App\Mail\InvoiceMail;
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 SendInvoiceEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public Invoice $invoice) {}

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

4. Dispatch the job

// In your controller
SendInvoiceEmail::dispatch($invoice);

// With a delay (send after 5 minutes)
SendInvoiceEmail::dispatch($invoice)->delay(now()->addMinutes(5));

// On a specific queue
SendInvoiceEmail::dispatch($invoice)->onQueue('emails');

Handling Failures

Jobs fail. Networks go down, APIs return errors, memory runs out. Laravel gives you tools to handle this gracefully.

Retry attempts

class SendInvoiceEmail implements ShouldQueue
{
    // Retry up to 3 times
    public int $tries = 3;

    // Wait 60 seconds between retries
    public int $backoff = 60;
}

The failed() method

public function failed(\Throwable $exception): void
{
    // Notify the user, log the error, send a Slack alert...
    Log::error('Invoice email failed', [
        'invoice_id' => $this->invoice->id,
        'error' => $exception->getMessage(),
    ]);
}

Checking failed jobs

# See all failed jobs
php artisan queue:failed

# Retry a specific failed job
php artisan queue:retry {id}

# Retry all failed jobs
php artisan queue:retry all

Failed job monitoring dashboard

Multiple Queues: Prioritization

Not all jobs are equal. A password reset email is more urgent than a weekly report. Use multiple queues to prioritize:

// High priority job
SendPasswordResetEmail::dispatch($user)->onQueue('high');

// Low priority job
GenerateWeeklyReport::dispatch()->onQueue('low');

Run workers with priority order:

php artisan queue:work --queue=high,low

The worker processes all high jobs before touching low ones.

Running the Queue Worker

Local development

php artisan queue:work

Production (with Supervisor)

In production, you need a process manager to keep the worker running. Supervisor is the standard choice.

; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/your-app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/your-app/storage/logs/worker.log
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*

Horizon: Queue Monitoring for Redis

If you’re using Redis, install Laravel Horizon for a beautiful dashboard to monitor your queues:

composer require laravel/horizon
php artisan horizon:install
php artisan horizon

It gives you real-time metrics: throughput, job duration, failure rates, and more.

Quick Reference

ScenarioSolution
Send email in backgroundShouldQueue job
Retry on failure$tries + $backoff
Urgent vs non-urgent jobsMultiple named queues
Keep worker runningSupervisor
Monitor Redis queuesLaravel Horizon
Delay a job->delay(now()->addMinutes(5))

Queues are one of those features that seem complex until you use them once. After that, you’ll find yourself reaching for them constantly. Start with a simple job, run it in the background, and watch your app feel instantly faster.

Boris Lukrece
Boris Lukrece

@borislukrece

Web developer based in Côte d’Ivoire 🇨🇮 — I build SaaS products, web tools, and community platforms. Specialized in Laravel, Livewire & Tailwind CSS. Currently shipping an HR management SaaS. I write about real dev experiences, not just theory. 👨🏾‍💻

Comments

0

Sign in to leave a comment

No comments yet. Be the first to comment!