appstract/nova-horizon
Displays Laravel Horizon queue metrics inside Laravel Nova, so you can monitor workload, jobs per minute, wait times, and failed jobs from your Nova dashboard without switching tools.
Architecture fit
appstract/nova-horizon is a thin Nova tool/card wrapper over Laravel Horizon's metric repositories. It adds no new infrastructure, no datastore, no queue middleware, and no schema changes. The entire surface is a read-only panel rendered inside the Nova admin UI that reads what Horizon has already computed and sampled (~1 minute intervals).
That means the architectural question is not "does this fit our system" but "does our system already exist." The package delivers zero value unless all three of the following are true:
| Precondition | Why it gates adoption |
|---|---|
| Laravel Nova 4.x or 5.x, licensed | It's a Nova tool; without Nova there is nothing to render it |
| Laravel Horizon backed by Redis | Metrics come from Horizon's repositories; DB/SQS queues have no Horizon data |
| Users with Nova access but no Horizon/SSH access | The only real ergonomic win — otherwise /horizon already serves them |
If any precondition fails, this is a stop, not a workaround. The package is a convenience layer over an authoritative system, not a replacement for one.
Integration feasibility
Low effort, high reversibility. The install path is four steps: composer require, vendor:publish the provider, register new NovaHorizon in NovaServiceProvider::tools(), and run nova:publish plus an asset build if compiled JS isn't shipped. Rollback is symmetrical and clean: remove the tool registration, composer remove the dependency. No migrations, no data to clean up, no job-payload side effects. That reversibility is the strongest argument for a timeboxed staging spike rather than an up-front architecture debate.
Technical risk
The risks are not "will it work" risks. They are "will it keep working and can we trust it" risks, and they concentrate in five places:
/horizon on a real environment" must be a recurring release step, not a one-time check.viewNova gate. Failed-job and exception views routinely contain serialized payloads — request bodies, customer IDs, occasionally tokens. Exposing queue health to every Nova user is a data-exposure decision, not a UI decision.environments entries, the card must render all of it or you risk decisions on partial data. Unverified topology support is a fork trigger, not a squint-and-accept.Key questions to resolve before committing
src/NovaHorizon.php call — MetricsRepository/JobRepository, or raw horizon:queue:* Redis keys? The latter breaks on Horizon internals changes.canSee() we can override with our own Gate, or must we patch the source?config/horizon.php — supervisors, connections, environments?Stack fit
Target state is Laravel + Nova (licensed) + Horizon on Redis, with a support/ops audience that lives in Nova daily and touches Horizon rarely or never. The intended workflow is explicit:
Slack alert → open Nova → Horizon tool → identify backlogged queue →
open /horizon (via ops gate) → inspect failed job, retry or investigate
The card answers "what's happening" for 80% of triage without SSH. /horizon retains the "do something about it" role. Positioning matters: this is a glance, not a pager, and not a source of truth. Horizon's native notifications and long-wait detection stay wired to their own path.
Migration path
There is none in the schema sense — nothing to move, nothing to backfill. The migration path is organizational: route a subset of queue questions from "ask an engineer" to "check Nova," then measure whether that actually happens. Rollback is removing two lines of registration and one Composer dependency.
Compatibility
Pin exact versions in composer.json — no caret ranges on either this package or Nova/Horizon. Add a documented re-validation step to the platform upgrade runbook: no Nova or Horizon major ships without re-validating this card against /horizon on a real environment. Verify nova:publish runs against the correct Nova major; if the sidebar entry appears but the page is blank, the browser console and js/nova-horizon.js are the first stops.
Sequencing
Gate::define('viewQueueHealth', ...) in AuthServiceProvider and confirm the tool respects it. Do this before the spike, not after — it changes whether adoption is viable at all./horizon on the same environment. Queue names, supervisor names, throughput, wait time, failed-job counts. Any mismatch ends the spike.config/horizon.php, including multi-connection/multi-supervisor if applicable. Test explicitly with Redis down.internal/nova-horizon or build a minimal custom card (~1 day using Laravel\Horizon\Contracts\*). Do not open upstream PRs for anything beyond compatibility patches.Maintenance
Steady-state cost is near zero. Upgrade cost is real: budget roughly 0.5–1 day per Nova or Horizon major. There is no vendor, no SLA, and unpredictable upstream responsiveness — the ownership posture is effectively "we maintain it," and that is a resourcing decision, not a procurement one. Assign a named internal owner explicitly; unowned community packages in the admin path rot silently.
Support
Community-only. Low adoption (77 stars, 0 dependents) means issues are unlikely to be pre-solved by others. Internal support burden is small but non-transferable: whoever owns it must know the /horizon cross-check procedure. Users need almost no training — the card is self-evident, and the ramp-up is one sentence of context ("this mirrors /horizon, which stays authoritative").
Scaling
The package itself doesn't scale — it's a rendered snapshot. What scales is concern about load: card renders hit Redis through Horizon's repositories, so query cost per render and refresh cadence need inspection, particularly if multiple stakeholders keep the tool open. Horizon's ~1-minute sampling means no historical depth and no real-time guarantees. At larger queue volumes, the card degrades into "directionally useful" rather than "operationally precise," and the authoritative surface takes over.
Failure modes
| Failure | Symptom | Severity | Mitigation |
|---|---|---|---|
| Redis/Horizon unreachable | 500 taking down the Nova dashboard | High | Test explicitly; wrap registration in try/catch or a Horizon-health gate |
| Horizon contract drift | Numbers render but are wrong | High (silent) | Cross-check vs /horizon at every major |
| Nova major upgrade | Blank page / missing JS asset | Low (visible) | Re-validate; check nova:publish ran against correct major |
| PII exposure via failed jobs | Payloads visible to broader Nova audience | High | Gate + security review; possibly omit the panel |
| Asset bundle collision | Sidebar entry with blank page | Low | Browser console; verify compiled JS |
Strategic caveat: if the platform team is standardizing on Laravel Pulse, Datadog, Grafana, or Sentry, do not adopt this mid-migration. It creates a second, weaker source of truth and fragments observability ownership for a convenience card.
Recommendation: a 1–2 day timeboxed staging spike, gated behind an ops policy, with a pre-agreed fork-or-rebuild decision point at the end. Adopt if the card covers ~80% of the read-only need and the topology check passes; rebuild a minimal custom Nova card if policy control, multi-connection coverage, or metric accuracy falls short. Both paths cost about the same; the difference is who owns the source.
How can I help you explore Laravel packages today?