Skip to content

Laravel Queues Deep — Job Processing and Queue Workers

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Laravel Queues Deep. We cover key concepts, practical examples, and best practices to help you master this topic.

Laravel queues process time-consuming tasks asynchronously with support for Redis, database, SQS, and Beanstalkd drivers, job batching, Rate Limiting, and Horizon monitoring.

What You'll Learn

By the end of this tutorial, you'll create and dispatch jobs, configure queue drivers, implement job batching, handle failed jobs, and tune worker processes for performance.

Why Queues Matter

Queues improve user experience by moving slow operations like email sending, image processing, and API calls out of the request lifecycle.

Real-World Use

An e-commerce application queues order confirmation emails, generates invoice PDFs, syncs products to a search engine, and processes payment Webhooks, all through Redis queues.

Queues Path

flowchart LR
  A[Laravel Framework] --> B[Queues]
  B --> C[Redis Queue]
  B --> D[Jobs]
  B --> E[Horizon]
  B --> F[Batch Jobs]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Creating Jobs

Generate and implement a job class.

php artisan make:job ProcessOrder
<?php
namespace App\Jobs;
use App\Models\Order;
use App\Services\PaymentGateway;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessOrder implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    public int $timeout = 120;
    public int $tries = 3;
    public int $backoff = 5;
    public function __construct(
        public Order $order,
    ) {}
    public function handle(PaymentGateway $gateway): void {
        try {
            $gateway->charge($this->order);
            $this->order->markAsPaid();
        } catch (PaymentException $e) {
            if ($this->attempts() >= $this->tries) {
                $this->order->markAsFailed($e->getMessage());
            }
            throw $e;
        }
    }
    public function failed(\Throwable $e): void {
        Log::error("Order processing failed", [
            "order_id" => $this->order->id,
            "error" => $e->getMessage(),
        ]);
    }
}

Dispatching Jobs

Dispatch jobs in various ways.

<?php
// Basic dispatch
ProcessOrder::dispatch($order);
// Delayed dispatch
ProcessOrder::dispatch($order)->delay(now()->addMinutes(30));
// Custom queue
ProcessOrder::dispatch($order)->onQueue("high");
// Custom connection
ProcessOrder::dispatch($order)->onConnection("redis");
// Dispatch chain
Bus::chain([
    new ProcessOrder($order),
    new GenerateInvoice($order),
    new SendConfirmationEmail($order),
])->dispatch();

Job Batching

Group jobs into batches with completion callbacks.

<?php
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
$batch = Bus::batch([
    new ProcessFile($file1),
    new ProcessFile($file2),
    new ProcessFile($file3),
])->then(function (Batch $batch) {
    Log::info("All files processed");
})->catch(function (Batch $batch, Throwable $e) {
    Log::error("File processing failed", ["error" => $e->getMessage()]);
})->finally(function (Batch $batch) {
    Cache::forget("processing_files");
})->dispatch();
return redirect()->route("files.status", ["batchId" => $batch->id]);

Queue Worker Configuration

Configure queue workers for production.

php artisan queue:work redis --queue=high,default --tries=3 --timeout=60 --sleep=3 --max-time=3600
<?php
// config/queue.php
"redis" => [
    "driver" => "redis",
    "connection" => "default",
    "queue" => env("REDIS_QUEUE", "default"),
    "retry_after" => 90,
    "block_for" => 5,
    "after_commit" => true,
],

Common Mistakes

1. Serializing Large Objects

Jobs serialize constructor data. Large models with relations increase Redis memory. Use only the model ID.

2. Not Handling Max Attempts

Without tries, jobs retry forever. Set tries and implement failed() to handle ultimate failures.

3. Ignoring after_commit

Without after_commit, jobs may dispatch before a model exists in the database.

4. Overlapping Queue Workers

Multiple workers processing the same queue can overload resources. Use Horizon for worker management.

5. Not Setting Timeout

Jobs that hang without a timeout occupy workers. Always set a reasonable timeout.

Practice Questions

1. What is a queue job?

A class that encapsulates a task to be processed asynchronously by a worker.

2. How do you handle a failed job?

Implement the failed() method or configure failed_jobs table and queue:failed command.

3. What is job batching?

Grouping jobs that dispatch together with completion callbacks.

4. What does after_commit do?

Delays job dispatch until the database Transaction is committed.

5. Challenge: Create a job with retry logic and failure handling.

<?php
class SyncProduct implements ShouldQueue {
    public int $tries = 5;
    public int $backoff = [10, 30, 60, 120, 300];
    public function handle(): void {
        $response = Http::post(config("services.sync.url"), $this->product->toArray());
        if ($response->failed()) {
            throw new \Exception("Sync failed: " . $response->body());
        }
    }
    public function failed(): void {
        $this->product->markSyncFailed();
        Notification::send(Admin::first(), new SyncFailed($this->product));
    }
}

FAQ

What queue driver should I use?

Redis for production. Database for development. SQS for serverless.

How many workers should I run?

Start with 1-2 per CPU core. Monitor queue length and worker utilization.

What happens if a job fails?

It retries based on tries/backoff. After max attempts, failed() is called and it goes to failed_jobs.

Can I prioritize queues?

Yes. Specify queue order in queue:work --queue=high,default.

Should I use sync driver in development?

Yes. sync processes jobs immediately without a queue worker.

Mini Project: Order Processing Pipeline

Build a complete order processing pipeline with queues.

<?php
class PlaceOrder {
    public function __invoke(array $orderData): void {
        DB::transaction(function () use ($orderData) {
            $order = Order::create($orderData);
            Bus::batch([
                new ProcessPayment($order),
                new ReserveInventory($order),
            ])->then(function () use ($order) {
                SendOrderConfirmation::dispatch($order);
            })->catch(function () use ($order) {
                $order->markAsFailed();
            })->dispatch();
        });
    }
}

What's Next

Laravel Horizon Laravel Broadcasting Laravel Notifications

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro