# Laravel queues in production

> Articles on Laravel queues under real traffic: retries and backoff, timeout vs retry_after, unique locks, job chains, rate limits and Horizon.

Source: https://boring-observability.dev/blog/topic/laravel-queues
Section: Laravel queues — the Skyline blog
Updated: 2026-10-19

---

Dispatching a Laravel job is one line. Most of what goes wrong happens after it, between a worker reserving the job and the job being deleted, and it tends to surface only once there is enough traffic for two workers to touch the same thing.

These posts follow a job through that gap. [The life of a queued job](https://boring-observability.dev/blog/laravel-queue-job-lifecycle) has the whole state machine in one place. The others each take one setting or feature (`retry_after`, `ShouldBeUnique`, backoff, chains, Horizon's balancing) and show what it does when the assumptions behind it stop holding.

For changes to the queue in the framework itself, the [Laravel queue changelog](https://boring-observability.dev/laravel/queue-changelog) lists every queue-related commit since Laravel 13.0.


## Posts

### [Handling 429 Too Many Requests in Laravel Queued Jobs](https://boring-observability.dev/blog/laravel-429-retry-after-queued-jobs.md)

Published 2026-10-19. A vendor API answers 429 and says how long to wait, and Laravel ignores it. Reading Retry-After in every form vendors send, releasing a job without spending its tries, and a job middleware that makes one 429 pause every job for that API.

### [Laravel Job Chains at Scale: Why the First Chain Finishes Last](https://boring-observability.dev/blog/laravel-job-chains-at-scale.md)

Published 2026-10-07. Dispatch 300 five-job chains on 10 workers and the first one finishes after 40 minutes, not two. Where the framework queues each next link, two ways to keep chains in order without the wait, and how a ShouldBeUnique link silently ends other chains.

### [Laravel Horizon Alerting: What Horizon Sends on Its Own and What Prometheus Adds](https://boring-observability.dev/blog/laravel-horizon-alerting.md)

Published 2026-09-30. Horizon ships one notification, and it cannot fire when Horizon is down. Which alert rules Spatie's Prometheus gauges support, which ones need a counter exporter, how to carry them into Grafana, and the failures that never show up in a metric at all.

### [Laravel Job Retries and Backoff with ShouldBeUnique and WithoutOverlapping](https://boring-observability.dev/blog/laravel-job-retries-backoff-unique-locks.md)

Published 2026-09-24. A retried job is released, not dispatched again, so each lock behaves differently while it waits out its backoff. What ShouldBeUnique, ShouldBeUniqueUntilProcessing and WithoutOverlapping do in that gap, and how overlap releases and retryUntil() bend the backoff itself.

### [Dispatching Laravel Jobs After the Transaction Commits](https://boring-observability.dev/blog/laravel-dispatch-after-commit.md)

Published 2026-09-15. A job dispatched inside a transaction can reach a worker before its rows commit. Where to turn on afterCommit, how it behaves with nested transactions, deadlock retries and ShouldBeUnique, and why Queue::fake() won't catch a rollback.

### [Laravel Horizon in Prometheus: Why Exported Throughput Reads Low](https://boring-observability.dev/blog/laravel-horizon-prometheus-grafana.md)

Published 2026-09-13. Horizon deletes its throughput hash on every snapshot, so a Prometheus exporter that reads it loses the jobs processed between the last scrape and the reset. Why the existing exporters undercount, and a two-file counter exporter with the PromQL and alert rules to go with it.

### [What Happens When You Clear a Queue in Laravel?](https://boring-observability.dev/blog/draining-a-laravel-redis-queue-backlog.md)

Published 2026-09-11. Clearing a Laravel queue deletes the payloads but not the ShouldBeUnique locks they hold, and with the default uniqueFor those locks never expire. How to see what is in a backlog, what queue:clear deletes, and how to drain it without leaving a job that can't be dispatched again.

### [The Life of a Laravel Queued Job: Every State, Every Transition](https://boring-observability.dev/blog/laravel-queue-job-lifecycle.md)

Published 2026-09-01. For most of its life a queued job is a JSON string moving between a Redis list and two sorted sets. The lifecycle in order: what dispatch writes, why exactly one worker gets it, why the attempt is spent at pop, and how a job survives a deploy.

### [Laravel Job timeout vs retry_after: The Ordering Rule Nothing Enforces](https://boring-observability.dev/blog/laravel-queue-timeout-retry-after.md)

Published 2026-08-29. Two numbers in two config files decide whether a job runs once or twice, and nothing in Laravel checks their order. What retry_after leases and why equal values already fail. Sometimes the symptom is a failure recorded for work that succeeded.

### [Rate-Limited APIs and Laravel Queues: One Request at a Time, Without Starving the Rest](https://boring-observability.dev/blog/laravel-queue-rate-limiting-concurrency.md)

Published 2026-07-14. "60 requests a minute" and "no parallel requests" are different limits, handled in different places in Laravel. RateLimited, WithoutOverlapping, Redis funnels and single-worker supervisors compared, and how to let urgent jobs go first without breaking the limiter.

### [Laravel Background Jobs: 12 Best Practices for Production Queues](https://boring-observability.dev/blog/laravel-background-jobs-best-practices.md)

Published 2026-06-30. Many job bugs come from assuming a job runs once, on time, on the code that dispatched it. In development that holds; under production load it doesn't. Twelve practices for Laravel queued jobs, ending with what happens to queued payloads when you deploy a change to the job class.

### [Laravel Horizon Queue Balancing: Idle Workers vs. Starved Queues](https://boring-observability.dev/blog/laravel-horizon-queue-balancing-tradeoffs.md)

Published 2026-06-23. Every Horizon supervisor makes you choose: turn balancing on and pay for a floor of idle workers, or turn it off and let a busy queue starve the ones listed after it. How both costs grow with the number of queues, and how Skyline's weighted queues avoid them.

### [Laravel ShouldBeUnique: What Breaks Under Real Traffic](https://boring-observability.dev/blog/laravel-job-uniqueness-controls.md)

Published 2026-06-12. ShouldBeUnique and WithoutOverlapping solve different problems and fail in different ways. What each one locks and when, and the failures that show up only at volume: dispatches dropped without an error, chains and retries that skip the lock, and locks that never expire.
