Laravel Queue Backpressure &amp; Dead-Letter Strategies | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Laravel Queues at Scale: Backpressure, Overflow Routing, and Dead-Letter Strategies

 Laravel Queues at Scale: Backpressure, Overflow Routing, and Dead-Letter Strategies
====================================================================================

 Beyond basic queue configuration lies a set of production patterns—backpressure detection, overflow routing to fallback queues, and structured dead-letter handling—that keep high-throughput Laravel apps stable under load.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 17 Jun 2026 · Updated 17 Jun 2026 · 3 min read

ShareCopy linkCopied

 ![Laravel Queues at Scale: Backpressure, Overflow Routing, and Dead-Letter Strategies](https://cdn.msaied.com/221/40080cae015b9b54cba4e665a16dbe7e.png) 

  On this page +1. [The Problem Nobody Talks About](#the-problem-nobody-talks-about)
2. [Detecting Backpressure Before It Hurts](#detecting-backpressure-before-it-hurts)
3. [Overflow Routing: Shedding Load Gracefully](#overflow-routing-shedding-load-gracefully)
4. [Structured Dead-Letter Handling](#structured-dead-letter-handling)
5. [Step 1 — Capture rich context on failure](#step-1-capture-rich-context-on-failure)
6. [Step 2 — Replay from the dead-letter store](#step-2-replay-from-the-dead-letter-store)
7. [Combining the Pieces](#combining-the-pieces)
8. [Takeaways](#takeaways)

 The Problem Nobody Talks About
------------------------------

Most Laravel queue tutorials stop at `php artisan queue:work` and a basic Horizon config. That works fine until your queue depth spikes to 50 000 jobs and workers start thrashing, retrying aggressively, and hammering your database. The real craft is in *backpressure*, *overflow routing*, and *dead-letter handling*.

---

Detecting Backpressure Before It Hurts
--------------------------------------

Backpressure means your producers are outpacing your consumers. The earliest signal is queue depth. Poll it on a schedule and react before workers drown:

```php
// app/Console/Commands/MonitorQueueDepth.php
public function handle(Queue $queue): void
{
    $depth = $queue->size('default');

    if ($depth > 10_000) {
        // Emit a metric, fire an alert, or flip a feature flag
        Cache::put('queue:backpressure', true, now()->addMinutes(5));
        Log::warning('Queue backpressure detected', ['depth' => $depth]);
    } else {
        Cache::forget('queue:backpressure');
    }
}

```

Schedule this every minute. Pair it with a Horizon metric or a Prometheus exporter so your alerting fires *before* users notice.

---

Overflow Routing: Shedding Load Gracefully
------------------------------------------

When backpressure is active, route lower-priority work to a slower `overflow` queue instead of blocking the primary one. A middleware on the job itself is the cleanest place to enforce this:

```php
// app/Jobs/Middleware/OverflowRouter.php
class OverflowRouter
{
    public function handle(object $job, callable $next): void
    {
        if (Cache::get('queue:backpressure') && method_exists($job, 'onQueue')) {
            // Re-dispatch to overflow and bail out of current attempt
            dispatch($job)->onQueue('overflow');
            return;
        }

        $next($job);
    }
}

```

Attach it selectively to non-critical jobs:

```php
public function middleware(): array
{
    return [new OverflowRouter];
}

```

In Horizon, give `overflow` a single slow worker so it drains without competing for resources:

```php
// config/horizon.php
'overflow' => [
    'connection' => 'redis',
    'queue' => ['overflow'],
    'balance' => 'simple',
    'processes' => 1,
    'tries' => 3,
],

```

---

Structured Dead-Letter Handling
-------------------------------

Laravel's `failed_jobs` table is a graveyard, not a strategy. Treat it as a first-class queue by wrapping failure logic in a dedicated handler.

### Step 1 — Capture rich context on failure

```php
// app/Jobs/Concerns/RecordsFailureContext.php
trait RecordsFailureContext
{
    public function failed(Throwable $e): void
    {
        DeadLetterEntry::create([
            'job_class' => static::class,
            'payload' => $this->toDeadLetterPayload(),
            'exception' => $e->getMessage(),
            'failed_at' => now(),
        ]);
    }

    abstract protected function toDeadLetterPayload(): array;
}

```

### Step 2 — Replay from the dead-letter store

```php
// app/Console/Commands/ReplayDeadLetters.php
public function handle(): void
{
    DeadLetterEntry::whereNull('replayed_at')
        ->chunkById(100, function ($entries) {
            foreach ($entries as $entry) {
                $job = new $entry->job_class(...$entry->payload);
                dispatch($job)->onQueue('overflow');
                $entry->update(['replayed_at' => now()]);
            }
        });
}

```

This gives you a controlled, auditable replay path instead of blindly running `queue:retry all`.

---

Combining the Pieces
--------------------

The full flow looks like this:

1. A scheduled command monitors queue depth every minute.
2. When depth exceeds a threshold, a cache flag signals backpressure.
3. Non-critical jobs detect the flag via middleware and re-dispatch to `overflow`.
4. Jobs that exhaust retries write structured records to `dead_letter_entries`.
5. An operator-triggered command replays dead letters through `overflow` at a controlled rate.

---

Takeaways
---------

- **Poll queue depth** on a schedule; don't wait for Horizon alerts alone.
- **Job middleware** is the right abstraction for overflow routing — it keeps the logic out of your business code.
- **Dead-letter tables** with rich context beat the default `failed_jobs` table for replay and auditing.
- **Overflow queues** with a single worker act as a pressure valve without dropping work.
- **Chunk-based replay** prevents a flood of retries from re-triggering the same backpressure you just escaped.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [queues](https://www.msaied.com/public/articles?search=queues)
- [reliability](https://www.msaied.com/public/articles?search=reliability)
- [scalability](https://www.msaied.com/public/articles?search=scalability)

 Frequently asked questions 
---------------------------

  Why not just increase worker count to handle backpressure?Adding workers helps up to the point where your database or downstream services become the bottleneck. Beyond that, more workers increase contention and can make things worse. Overflow routing buys time without adding load to already-saturated resources.

   Is there a risk of a job being dispatched twice with the OverflowRouter middleware?Yes, if the middleware re-dispatches and then throws before returning, the original job may retry. Guard against this by catching dispatch exceptions and marking the job as released rather than failed, or by making your jobs idempotent using a unique ID stored in cache.

   Can this pattern work with database queues instead of Redis?Yes. Queue depth via `Queue::size()` works with the database driver. The overflow and dead-letter patterns are driver-agnostic. Redis is preferred at scale because depth queries are O(1) rather than a COUNT on a large table.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleRead Model Projections in Laravel Without Full Event Sourcing](https://www.msaied.com/public/articles/read-model-projections-in-laravel-without-full-event-sourcing) [Next articleThe artisan dev Command and What's New in Laravel 13.16.0](https://www.msaied.com/public/articles/the-artisan-dev-command-and-whats-new-in-laravel-13160)  

   On this page
-------------

1. [The Problem Nobody Talks About](#the-problem-nobody-talks-about)
2. [Detecting Backpressure Before It Hurts](#detecting-backpressure-before-it-hurts)
3. [Overflow Routing: Shedding Load Gracefully](#overflow-routing-shedding-load-gracefully)
4. [Structured Dead-Letter Handling](#structured-dead-letter-handling)
5. [Step 1 — Capture rich context on failure](#step-1-capture-rich-context-on-failure)
6. [Step 2 — Replay from the dead-letter store](#step-2-replay-from-the-dead-letter-store)
7. [Combining the Pieces](#combining-the-pieces)
8. [Takeaways](#takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
