Skyline

Laravel Skyline vs Deck

Deck records what your jobs did, in your database. Skyline changes what your queues do, in Redis. They answer different questions.

Updated · Boring Observability

[ the bottom line ]

These are not really alternatives. Deck answers "what has this job class been doing?" by keeping durable history in your database. Skyline answers "what do I do about this queue right now?" by replacing Horizon itself. If your recurring problem is losing job history to Redis retention, Deck is the closer fit — and it is free. If it is being unable to act on a live queue without SSH, that is Skyline.

Deck
A second dashboard beside Horizon that writes a durable job-execution log to your database. Free, MIT (optional paid Deck Cloud)
Laravel Skyline
A commercial fork of Laravel Horizon that replaces the package and adds production queue operations. Annual subscription · MIT-licensed source

Deck and Laravel Skyline are both aimed at people running Laravel queues in production who have found Horizon's dashboard insufficient — but they diverge immediately after that, and a shortlist that treats them as substitutes will pick the wrong one. Deck is explicit about this in its own documentation: "Deck does not replace Horizon. Keep php artisan horizon in production." It installs beside Horizon, listens to queue events, and writes a durable execution log to your database, served from its own dashboard at /deck. Skyline replaces the laravel/horizon package itself and works on live Redis state from the dashboard at /horizon.

Put plainly: Deck is a record of what happened. Skyline is a set of controls for what is happening.

Two different layers#

Horizon's job history lives in Redis, governed by the trim retention settings in config/horizon.php. Those windows are measured in minutes and hours, because Redis is expensive memory rather than cheap disk. That produces the gap Deck exists to close: a completed job ages out, and with it any ability to answer "when did ProcessInvoice last succeed, and how long has it been taking?" — a question that arrives days after the evidence has expired.

Deck answers it by writing every execution to a table in your database, indexed by job class, queue, connection and tag, with retention you control and a deck:prune command to enforce it. That is a genuinely different thing from what Skyline does, and Skyline does not do it.

Skyline's gap is the other one. Horizon can show you that a queue has 40,000 jobs in it and cannot show you what they are; it can tell you a job is retrying and cannot run it now; it can pause every worker and cannot pause the one queue that is misbehaving. Those are all operations on live Redis state, which is reachable only from inside the Horizon package — which is why Skyline is a fork rather than a companion.

At a glance#

Capability Deck Skyline
Relationship to laravel/horizon Installs alongside it Replaces it
Dashboard A second one, at /deck Horizon's, at /horizon
Durable job history beyond Redis retention Yes — a table in your database No — Horizon's Redis retention
Per job-class stats (avg, p50, p95, failure rate) Yes No — aggregate metrics only
Stop a job that is already running Cooperative cancel, needs job-class changes No — reserved jobs are refused
Block a job class at dispatch during an incident Yes No
In-progress job progress reporting Yes No
Stale-job & unprocessed-queue alerts Yes, scheduled No — Horizon's long-wait notifications
Pause & resume a single queue No Yes
Run a delayed or retrying job immediately No Perform Now
Read the jobs waiting inside a queue, in order No Per-queue drill-down
Clear pending jobs from a queue Yes, from the Workers page Yes, per job or whole queue
Weighted queue processing No Yes
Front-of-queue dispatch No onFront()
Database migrations Yes — the log is the product None
Requirements PHP 8.3+, Laravel 11–13, a database PHP 8.2+, Laravel 11+
Price Free, MIT — optional paid Deck Cloud Annual subscription

Stopping a job: two different meanings#

Both tools claim something like "stop this job", and they mean genuinely different things. This is the single comparison most worth reading carefully, because the words are the same and the operations are not.

Deck cancels a running job, cooperatively, if you prepared it. You add Deck's Cancellable middleware to the job class and call JobCancellation::throwIfCancelled() at checkpoints inside handle(). Deck then sets a flag and the job bails out at its next checkpoint. Deck is candid that no PHP worker is force-killed and that pending cancellation on Redis queues is best-effort. The trade-off is real and reasonable: it can interrupt work already underway, but only in jobs you have edited to be interruptible, and only at the granularity of the checkpoints you wrote.

Skyline removes a job that has not started. It needs no changes to your job classes, and it deliberately refuses to touch a job a worker has already reserved — that request is rejected rather than left to produce a half-executed job. If a worker picks the job up between your click and the delete landing, the request fails visibly instead of silently doing nothing. In exchange, Skyline releases the job's ShouldBeUnique lock on the way out, which is the failure mode that makes deleting jobs from a dashboard dangerous in the first place.

Deck's class blocking has no equivalent in Skyline at all. Blocking SyncInventory::class for an hour while an upstream API is down, and having every dispatch intercepted rather than piling into a queue, is a genuinely useful incident tool that Skyline's per-queue pause only approximates — pausing a queue stops workers consuming it, but producers keep dispatching into it, so the backlog still grows.

Where Deck is the better answer#

  • Your history keeps evaporating. If the recurring pain is that Horizon has already trimmed the job you need to look at, the fix is a database, and Deck is a well-built one — for free.
  • You need per-class SLOs. Avg, p50, p95 and failure rate per job class, over a configurable window, is not something Skyline reports.
  • You want to be told, not to look. Deck's stale-job and unprocessed-queue alerts run on a schedule and notify. Skyline inherits only Horizon's long-wait notifications.
  • You run many services. Every Deck row is scoped by project and environment, and Deck Cloud exists to aggregate them. Skyline is per-application.
  • Budget is the constraint. Deck is MIT and free. That is not a small point and we are not going to pretend otherwise.

Where Skyline is the better answer#

  • The queue is on fire right now. Pausing one queue, reading what is actually inside it, and draining it are live-state operations on Redis. Deck's dashboard is a record; it cannot reorder or unblock the queue it is describing.
  • You have deployed the fix and want the retrying job to go now. Perform Now skips the backoff window. Nothing in Deck's feature set does this.
  • Your priority scheme starves a queue. Weighted processing and front-of-queue dispatch live in the worker loop, which only a package that replaces Horizon can reach.
  • You cannot take on new schema. Skyline adds no tables, no migrations and no pruning job. Deck's execution log grows with your throughput and is deliberately a database you now operate.
  • You want one dashboard. Deck is a second URL, a second mental model and a second set of access controls to reason about. Skyline is the /horizon your team already opens.

Can you run both?#

By design, nothing puts them in conflict. Deck mounts its own dashboard at /deck and records queue events through Laravel's own event system; Skyline is the Horizon install at /horizon; and because Skyline declares replace: laravel/horizon in Composer, Deck's Horizon requirement resolves against it the same way it would against upstream. Deck's failed-execution rows even link back into Horizon's failed-job store, which Skyline keeps unchanged.

Expected, not yet certified

Skyline is still pre-release, and we have not run the pairing in anger. Treat the above as the design intent of both packages rather than a tested guarantee, and try it on a staging environment first. If you do run them together, we would like to hear how it went.

Choosing between them#

Ask which sentence you have said out loud more often in the last six months.

  • "I can't tell when this job last ran successfully." — That is retention, and that is Deck.
  • "I need to stop this one queue without stopping everything, and I need to do it now." — That is live control, and that is Skyline.

If both, they are not mutually exclusive, and Deck costs nothing to add.

Common questions

Is Deck an alternative to Laravel Skyline?

Only partly. Deck is explicit that it does not replace Horizon — it installs beside it, listens to queue events and writes a durable execution log to your database, served from its own dashboard at /deck. Skyline replaces the laravel/horizon package itself and works on live Redis queue state from the dashboard at /horizon. The overlap is small: both can search job history and clear pending jobs from a queue. Everything else the two do is at a different layer.

Can I run Deck and Skyline at the same time?

By design, nothing puts them in conflict: Deck mounts its own dashboard at /deck and records queue events, while Skyline is the Horizon install at /horizon, and Deck's Horizon requirement resolves against Skyline's Composer replace declaration. Because Skyline is still pre-release we have not certified that pairing, so treat it as expected rather than guaranteed until we have.

What is the difference between Deck's cooperative cancellation and deleting a job in Skyline?

They act at opposite ends of a job's life. Deck can stop a job that is already running, but only if you have prepared it: you add its Cancellable middleware and call JobCancellation::throwIfCancelled() at checkpoints inside handle(), and cancellation is cooperative — no worker is force-killed. Skyline deletes jobs that have not started yet, needs no changes to your job classes, and explicitly refuses to delete a reserved job rather than leave a half-executed one behind.

Does Laravel Skyline keep durable job history like Deck does?

No. Skyline reads the same Redis-backed history Horizon does, governed by the same trim retention settings, so it inherits the same horizon: once a completed job ages out it is gone. Skyline adds structured job lifecycle logging to a log channel of your choice, which is a stream rather than a queryable table. If you need to answer "when did ProcessInvoice last succeed?" three weeks later, that is a database, and that is what Deck is for.

Does Deck add database tables?

Yes — a durable execution log is the point of it, so Deck ships migrations, grows a table proportional to your job throughput, and provides a deck:prune command to enforce its retention window. Skyline adds no schema and no migrations at all, which is also why it cannot offer durable history.

Queue control, not just queue monitoring.

Skyline is a drop-in replacement for Laravel Horizon that lets you act on what you see — pause a queue, jump a job to the front, drain a backlog.

Sign up for early access