Originally published at hafiz.dev
Laravel Cloud shipped Managed Queues on May 26, and the pitch is hard to ignore: queue workers that scale up when jobs pile up, scale to zero when there's nothing to do, and a built-in dashboard for failed jobs. No Supervisor configs, no Redis tuning, no horizon:terminate in your deploy script.
If you're running Horizon today, the obvious question is: should you care? I went through the docs, the launch post, and the pricing model to answer that properly. The short version: Managed Queues solve real problems, but Horizon users give up more than the announcement suggests. There's a 15-minute delay cap that will silently break a lot of apps, and the Redis features Horizon users lean on don't exist in the SQS world.
Here's the honest breakdown.
What Managed Queues Actually Are
First, the architecture, because it explains everything else. With Managed Queues, Laravel Cloud becomes your queue driver. Under the hood it's SQS: your app needs aws/aws-sdk-php in its composer.json, and Cloud provisions the queue, configures access, and reads queue depth directly from SQS without going through your application.
Every worker runs in its own isolated container with guaranteed memory. You pick a tier from 256 MiB up to 8 GiB, and CPU scales with it. When jobs arrive, Cloud spins up workers based on queue pressure (depth plus message age, not just depth). When the queue drains, workers scale back to zero and billing stops.
Compare that to Horizon (v5.47 as of June 2026): a free package, Redis only, running on infrastructure you provision. You define worker pools and balancing strategies in config/horizon.php, keep the master process alive with Supervisor, and remember to call horizon:terminate on every deploy. Horizon gives you enormous control. It also gives you all the operational responsibility.
That's the actual trade: control and Redis features versus zero infrastructure and pay-per-second billing.
What You Get
Scale to zero. This is the headline. A Horizon setup on a Forge-managed VPS or Hetzner box costs the same whether it processes a million jobs or none. Managed queue workers cost nothing while idle. For side projects, staging environments, and apps with bursty workloads, that's a real difference.
True worker isolation. Each worker gets its own container with guaranteed memory. One job blowing past its memory limit kills that one worker, not its neighbors. With Horizon, workers share the box: one memory-hungry job can take down everything on the same server, which is exactly the failure mode I covered when the AI SDK's default config starved a Horizon queue.
Burst absorption without capacity planning. Cloud scales from zero to your max worker cap automatically. With Horizon, maxProcesses is bounded by the RAM you bought. Dispatching 10,000 jobs at once means either provisioning headroom you pay for all month, or watching the backlog drain slowly. Processing 10,000 tasks is a sizing exercise on Horizon. On Managed Queues it's a config field.
A failed jobs dashboard your support team can use. Failed jobs surface with full payload, exception, and stack trace, and anyone with environment access can retry with one click. No php artisan queue:retry over SSH. For teams where non-developers field "my export never arrived" tickets, this matters more than it sounds.
Pause and purge from the UI. Pause terminates workers immediately and stops billing while jobs accumulate safely. Note the difference from Horizon's queue:pause: Horizon's version keeps workers alive (and on your server, still costing you), Cloud's version actually stops the meter.
What You Give Up
This is the part the launch post doesn't dwell on.
Delayed jobs are capped at 15 minutes. This is the biggest gotcha and it deserves the bold. SQS has a hard 15-minute delay limit, and Managed Queues inherit it. If your app does ->delay(now()->addHour()) for a follow-up email, or schedules a retry for tomorrow morning, that breaks. Horizon on Redis delays jobs for as long as you want. Plenty of Laravel apps use long delays without thinking about it, and nothing in your code will warn you before you migrate. Audit every delay() and release() call before considering a move.
At-least-once delivery only. No strict ordering, no exactly-once processing. If a worker hits the visibility timeout, gets terminated during a deploy, or a queue is paused mid-flight, jobs can be redelivered. Your jobs must be idempotent. Good queue code should be idempotent anyway, but with Horizon on Redis many apps quietly get away with assuming a job runs once. On Managed Queues that assumption will eventually bite you.
Horizon's Redis toolbox doesn't come along. Tags for monitoring specific models or job types. Redis::funnel() and Redis::throttle() for rate limiting third-party API calls. Balancing strategies that shift workers between queues based on load. Per-queue wait time alerts via Slack or SMS. None of this exists on Managed Queues. The dashboard shows volume, duration, memory, and worker counts per queue, which is good operational visibility, but it's not Horizon's per-tag, per-job granularity. (For deeper tracing, Laravel's answer is pairing Cloud with Nightwatch, which is another subscription.)
One queue name per managed queue. Horizon lets one supervisor drain default,emails,exports with priorities. On Cloud, each queue name is its own managed queue with its own config. The Starter plan allows exactly 1 queue and 3 max workers, Growth allows 10 queues and 25 workers each, Business is uncapped (50 workers soft cap, raisable). If your app uses queue names for priority lanes, you'll restructure.
Cold starts. A queue that scaled to zero takes several seconds to spin up its first worker (around 10 seconds per the engineering team). For background reports nobody notices. For user-facing jobs like "your download is ready," a 10-second floor on top of job runtime is noticeable. Workers can't currently be kept warm; a configurable warm minimum is planned but not shipped.
Dashboard-only management, for now. No API or CLI for creating, pausing, or configuring queues at launch. If you manage infrastructure as code, that's a regression from a horizon.php file in version control.
You're on Laravel Cloud. Obvious, but worth saying: Managed Queues only exist as a platform feature of Laravel Cloud. Horizon runs anywhere Redis does.
The Pricing Math
Managed Queues bill two meters: worker compute per second (by memory tier) and queue operations at $1 per million. An operation is any API call against the queue: every dispatch, receive, delete, and every polling check at your configured interval (default 5 seconds). Payloads are measured in 64 KB chunks, with a 1 MiB max per job.
Run the numbers on a typical small production app: say 300,000 jobs a month, averaging 2 seconds each on a 256 MiB worker. That's roughly 600,000 worker-seconds, plus around a million operations (three per job, plus polling). You're looking at single-digit dollars per month, and a dev or staging environment that sits idle most of the day costs close to nothing.
Now the Horizon side: a €4.49/month Hetzner CX22 runs Horizon comfortably for that workload, flat rate, with the rest of the box free for your app. At steady, predictable volume, a fixed server stays hard to beat, and at high sustained volume the per-second meter can climb past a beefier fixed server. The crossover isn't a single number because it depends on job duration and memory tier, but the shape is clear: spiky and idle-heavy favors Managed Queues, flat and sustained favors a fixed worker.
That's also why Cloud itself still offers worker clusters (fixed workers running queue:work continuously) and recommends them for sustained, predictable throughput. Even inside Laravel's own platform, autoscaling isn't always the answer.
So Who Should Switch?
Stay on Horizon if: you use delays longer than 15 minutes anywhere, you depend on tags, Redis::throttle(), or balancing strategies, your throughput is steady enough that a fixed server is cheaper, or you simply aren't on Laravel Cloud and have no other reason to move. Horizon is mature, free, and v5.47 shows it's still actively maintained.
Use Managed Queues if: you're already on Cloud (it should be your default there over app cluster background processes), your workload is bursty or idle-heavy, you want support staff retrying failed jobs from a UI, or you're starting a new project and would rather never write a Supervisor config. Start with a single queue at 256 MiB, watch the memory chart, and adjust.
Don't switch mid-flight without an audit. The delay cap and at-least-once semantics are silent breaking changes. Grep for delay(, release(, and WithoutOverlapping and check every result before moving a production queue.
My take: Managed Queues are the right default for new apps on Cloud, and the failed jobs dashboard alone will sell it to teams. But Horizon is not obsolete. It's the more capable tool that costs you operational attention instead of per-second billing. The 15-minute delay cap is the dealbreaker to check first, before price, before features.
FAQ
Can I run Horizon on Laravel Cloud instead of Managed Queues?
Yes. Cloud's worker clusters let you self-manage queue:work processes with your own driver, and you can run Horizon against a Redis instance there. You give up scale-to-zero and the built-in failed jobs dashboard, but you keep Redis features and long delays.
Do Managed Queues work with Laravel 10?
No. The supported minimums are Laravel 11.53.1, 12.60.2, and 13.11.2, and your app must include aws/aws-sdk-php. Deploys that create a managed queue on an unsupported version fail with a clear error.
What happens to my failed_jobs table?
Managed Queues surface failed jobs in the Cloud dashboard automatically, with no failed jobs driver to configure. Failures are visible cross-queue with payload, exception, and stack trace, and you can retry or delete from the UI.
How do scheduled jobs with long delays work if there's a 15-minute cap?
They don't, directly. The workaround is restructuring: instead of dispatching with a long delay, store the intended run time and let the scheduler dispatch the job when it's due. It's a known SQS pattern, but it's code you have to write and test.
Is Horizon being deprecated?
No sign of that. Horizon v5.47.2 shipped in early June 2026 with support through Laravel 13. Cloud's own queue clusters were deprecated in favor of Managed Queues, but Horizon is a framework package, not a Cloud feature, and it remains the standard for self-hosted Redis queues.
Wrapping Up
Managed Queues are Laravel Cloud's strongest argument yet for teams that hate running queue infrastructure: real isolation, honest pay-for-what-runs billing, and a failed jobs story Horizon never had. Horizon remains the choice when you need Redis semantics, long delays, fine-grained control, or predictable flat costs. Know which list describes your app before you touch anything, starting with that 15-minute delay cap.
If you're weighing a move to Laravel Cloud or untangling a queue setup that's outgrown its config, let's talk.
Top comments (0)