# Skyline for Laravel vs Laravel Horizon

> Skyline is a fork of Horizon. Everything Horizon does still happens; the difference is what you are told about and what you can do about it.

Source: https://www.boring-observability.dev/skyline/vs/horizon
Section: Comparisons — Skyline for Laravel
Updated: 2026-09-27

---

**Bottom line:** Keep Horizon if watching your queues is all you need. It is free and official, and Skyline changes none of it. Choose Skyline when you want to be told: it alerts when Horizon stops running, a queue stalls, jobs fail in bulk or workers crash-loop, and again when the problem clears, where Horizon has one long-wait notification. Or when you keep needing to act on what the dashboard shows, such as pausing one queue during an incident, running a retrying job now or draining a backlog. Or when the problem is not on the dashboard at all: a ShouldBeUnique lock silently swallowing every dispatch, or the job class eating the memory your workers keep restarting over.

Skyline for Laravel is a commercial fork of Laravel Horizon that keeps every part of Horizon intact and adds three things on top of it. It tells you when something breaks: Horizon sends one notification, for a long wait, while Skyline alerts when Horizon stops running, a queue stalls, jobs fail in bulk or workers crash-loop, and says so again when it clears. It shows the things Laravel does quietly, such as a dispatch dropped over a held unique lock or a job failed before it ever ran. And it gives you the buttons to act: pause a queue, release a stuck lock, run a delayed job now, drain a backlog. Everything below is additive, and Skyline removes or changes no Horizon feature.

## At a glance

| Capability | Horizon | Skyline |
| --- | --- | --- |
| Drop-in for `laravel/horizon` (same config, same namespaces) | It *is* Horizon | Yes |
| Alert when Horizon stops running, a queue stalls, jobs fail in bulk or workers crash-loop | No, one long-wait notification | [Twelve checks, routed by severity](https://www.boring-observability.dev/skyline/docs/alerts) |
| Alert when a lock blocks a job, or a supervisor's timeout will run jobs twice | No | [`stranded_lock`, `misconfiguration`](https://www.boring-observability.dev/skyline/docs/alerts) |
| Recovery message when the problem clears | No, the long-wait notice never resolves | [Yes, with how long it lasted](https://www.boring-observability.dev/skyline/docs/alerts) |
| Quiet period after a deploy, and a repeat limit while it lasts | No | [Five minutes; repeats at most every 15](https://www.boring-observability.dev/skyline/docs/alerts#noise) |
| Alerts to a webhook (PagerDuty, Opsgenie, your own) | No, mail, Slack and SMS | [JSON webhook as well](https://www.boring-observability.dev/skyline/docs/alerts#webhook) |
| Throughput, runtime & failure metrics | Yes | Yes |
| Supervisors, balancing strategies, autoscaling, tags, notifications | Yes | Yes, unchanged |
| Trigger a delayed or retrying job immediately | No | [Perform Now](https://www.boring-observability.dev/skyline/docs/dashboard-operations) |
| Drill into the jobs waiting inside a queue | No | [Per-queue drill-down](https://www.boring-observability.dev/skyline/docs/dashboard-operations) |
| Delete a job, or empty a queue, from the UI | No | [Yes, with guards](https://www.boring-observability.dev/skyline/docs/dashboard-operations) |
| Pause & resume a single queue | No, the whole supervisor, from the CLI | [Per queue, from the dashboard](https://www.boring-observability.dev/skyline/docs/pausing-queues) |
| Pause & resume all of Horizon | `horizon:pause`, one host at a time | [A dashboard button that reaches every host](https://www.boring-observability.dev/skyline/docs/pausing-queues) |
| Proportional queue priority without starvation | No, strict priority only | [Weighted queues](https://www.boring-observability.dev/skyline/docs/weighted-queues) |
| Ad-hoc front-of-queue dispatch | No | [`onFront()`](https://www.boring-observability.dev/skyline/docs/front-of-queue-dispatching) |
| Search jobs by class name or argument value | No | One box over every listing, e.g. `checkout_id: 3` |
| Scheduled and retrying jobs told apart | No, one delayed list | Separate tabs, split on attempt count |
| Structured job lifecycle logging | No | [Every transition, incl. silent drops](https://www.boring-observability.dev/skyline/docs/job-lifecycle-logging) |
| See which `ShouldBeUnique` and `WithoutOverlapping` locks are held, and release a stuck one | No, find the key with `redis-cli` | [Locks & Limits screen](https://www.boring-observability.dev/skyline/docs/locks-and-limits) |
| Dispatches skipped because a unique lock was held | Not recorded anywhere | [Counted per job, hourly and daily](https://www.boring-observability.dev/skyline/docs/locks-and-limits) |
| `ShouldBeUnique` lock released on out-of-band removal or a pre-start failure | No, the lock wedges | [Released, owner-checked](https://www.boring-observability.dev/skyline/docs/unique-job-locks) |
| Debounced jobs (`#[DebounceFor]`) that were superseded | Counted as completed runs | [Logged as debounced, counted per job](https://www.boring-observability.dev/skyline/docs/locks-and-limits) |
| Jobs held back by a rate limiter or throttle | A release looks like any other | [Counted per limiter, with the reason](https://www.boring-observability.dev/skyline/docs/locks-and-limits) |
| Queue failures Laravel handles silently | No, or a misleading error | [`RetryWindowExpired`, expired reservations](https://www.boring-observability.dev/skyline/docs/troubleshooting) |
| What each job class costs in memory and CPU | No, runtime only | [Peak memory, heap growth, CPU](https://www.boring-observability.dev/skyline/docs/insights) |
| Why workers are being replaced | No | [Restarts by reason, and fleet memory & CPU](https://www.boring-observability.dev/skyline/docs/insights) |
| Prometheus scrape endpoint & Grafana dashboard | No | [Both, bundled](https://www.boring-observability.dev/skyline/docs/prometheus-metrics) |
| Let an AI agent inspect queues, jobs and metrics | No | [Read-only MCP server](https://www.boring-observability.dev/skyline/docs/mcp-server) |
| Price | Free, MIT | $139 once, a year of upgrades |

## What is identical

Skyline is a *rebased* fork: its `main` is a recent upstream Horizon tag with the Skyline commits replayed on top, and upstream releases are merged within 14 days. The parts of Horizon you already rely on run the same code as upstream, with no reimplementation to behave subtly differently:

- The `Laravel\Horizon\` namespace, and every class in it.
- `config/horizon.php`, including supervisors, balancing, autoscaling and `trim`.
- Every `php artisan horizon:*` command.
- The dashboard's mount point, `/horizon`, and the `viewHorizon` gate that protects it.
- Metrics snapshots, tags, long-wait notifications. With Skyline's alerts turned on, `queue_wait` takes over from the long-wait notification, using the same thresholds.

The [official Horizon documentation](https://laravel.com/docs/horizon) applies to Skyline in full, and this site documents only the differences.

## When switching is worth it

Horizon is the free, official package, and it does its job well. If watching your queues is all you need, keep it: Skyline changes nothing about that job, so paying for it would buy you nothing.

The case for switching usually shows up as a recurring frustration:

- **You heard the queues were down from a customer.** Horizon's only notification is a long wait, so nothing reports a master that is not running at all, a worker stuck on one job, or a class failing every run.
- **You have SSH'd into a worker box during an incident** to stop one queue, because the dashboard's only pause button stops all of them.
- **You have waited out a backoff window** after deploying a fix, watching a retrying job sit there because there is no way to say "run it now".
- **You have written a one-off Artisan command** to inspect or drain a queue, because the dashboard can count the backlog but not show you what is in it.
- **You have a queue that starves** under strict priority, and turning balancing on to fix it costs you a floor of idle workers you do not need.
- **You have had a job stop running with no error at all** — a `ShouldBeUnique` lock left held by a cleared queue or a job that failed before it started, silently swallowing every dispatch after it. Horizon shows nothing, because a skipped dispatch never reaches the queue.
- **You have watched workers restart all day** without being able to say which job class was eating the memory, because Horizon measures runtime and nothing else.

In each case Horizon either stays silent or shows you the problem and leaves the fix to a terminal. Skyline covers all seven.

## Where an APM fits

Skyline **runs entirely on your own servers**, with no agent and no data sent to a vendor. Its alerts cover the queue: Horizon down, stalled queues, failure spikes, crash-looping workers. Tools like Laravel Nightwatch, Datadog or Sentry cover your whole application, with exceptions, slow requests and traces Skyline never looks at. The two pair rather than compete, and once either one has paged you, Skyline is where you pause the queue, drain it or jump a job to the front.

## What the difference costs

Horizon is free and MIT-licensed. Skyline is a one-time $139 purchase that includes twelve months of upgrades. Two details of that licence usually go unstated in commercial forks:

- **There is no runtime licence check.** The licence authenticates Composer against a private registry. Nothing in the shipped package phones home or can interrupt job processing, so an expired upgrade year cannot take your queues down — every version published during it stays installable, and only releases made after it stop resolving until you renew.
- **The source is MIT-licensed**, as Horizon's is. What you are buying is access to the registry and the maintenance behind it.

## Keeping up with upstream

A fork can leave you stranded two Laravel majors behind if it quietly stops tracking upstream. Skyline is kept as a rebase, so taking a new Horizon release means replaying a small, known set of commits onto a new tag, and upstream releases are merged within 14 days. Skyline-only features are built on top of that current base.

## The cost of switching, and of switching back

Because Skyline declares `replace: laravel/horizon` and keeps the namespace, adopting it is a Composer change and a redeploy — no code changes, no migrations. Rolling back is the same operation in reverse, and it loses nothing but the Skyline-only views: there is no schema to unwind, and your queues and jobs are untouched. See [Migrating from Laravel Horizon](https://www.boring-observability.dev/skyline/docs/migrating-from-horizon).


## Common questions

### Should I use Laravel Horizon or Skyline for Laravel?

Use Horizon if watching the dashboard is all you need. It is free and official, and Skyline changes none of it. Choose Skyline when you need to be told when the queues stop working, since Horizon only notifies on a long wait and never says it recovered, or when you need to act on what the dashboard shows: pausing one misbehaving queue during an incident, running a retrying job immediately after deploying the fix, draining a backlog, or giving one queue priority without starving the others.

### Is Skyline for Laravel a drop-in replacement for Laravel Horizon?

Yes. Skyline keeps the Laravel\Horizon\ namespace and declares replace on laravel/horizon in Composer, so your existing config/horizon.php, service-provider customisations, gate definitions and horizon:* Artisan commands all keep working. The dashboard still mounts at /horizon. There are no migrations and no application code changes.

### Will Skyline for Laravel keep up with Laravel Horizon releases?

Skyline is a rebased fork rather than a divergent one: its main branch is a recent upstream Horizon tag with the Skyline commits replayed on top, and upstream releases are merged within 14 days. New Horizon versions, bug fixes and Laravel compatibility updates land on a current base rather than instead of one.

### Can I roll back from Skyline to Horizon?

Yes, and it is the same operation in reverse: require laravel/horizon again and drop the Skyline package. Skyline adds no database schema and stores its extra tracking data under the same Redis prefix, so a rollback loses the Skyline-only views and leaves your queues and jobs untouched.

### What does Skyline for Laravel show that Horizon cannot?

The failures Laravel handles without saying anything. A dispatch skipped because a ShouldBeUnique lock is held never reaches the queue, so Horizon has nothing to show; Skyline lists the held locks, flags the stranded ones, counts the dispatches each has skipped and gives you an owner-checked Release button. Jobs released by a rate limiter or throttle look like any other release to Horizon; Skyline attributes them per limiter. A job refused on pickup after its retryUntil() deadline is reported as RetryWindowExpired rather than "has been attempted too many times". And with insights enabled it records what each job class costs in memory and CPU, what the whole worker fleet is using, and why workers are being replaced — none of which Horizon measures.

### Does Laravel Horizon send alerts?

One kind. Horizon sends a LongWaitDetected notification over mail, Slack or SMS when a queue's wait passes its waits threshold, and never says when the wait ended. Skyline adds an alert pipeline with sixteen checks, including Horizon not running at all, a queue that has stopped moving, jobs failing in bulk, workers crash-looping and a stranded lock. Each condition has to hold before it notifies, sends a recovery message when it clears, stays quiet for five minutes after a deploy and is routed by severity to mail, Slack, SMS or a JSON webhook.

### How is Skyline different from Laravel Nightwatch or an APM?

Skyline covers the queue and nothing else. It runs on your own servers with no agent and no data egress, alerts on queue failures such as Horizon being down, a stalled queue or a failure spike, and gives you the controls to act on them. An APM covers the whole application, with exceptions, slow requests and traces. They pair rather than compete.
