mad-web/nova-horizon-link
A Laravel Nova package that adds a convenient link to your Laravel Horizon dashboard directly within Nova, giving developers quick access to queue monitoring, failed jobs, and metrics without leaving the admin panel.
Install the package in the same Laravel application that already runs both Nova and Horizon:
composer require mad-web/nova-horizon-link
The service provider is auto-discovered, so no manual registration is required in config/app.php on Laravel 5.5+.
Confirm Horizon is actually installed and reachable. This package only draws a link — it does not ship Horizon. You should already have laravel/horizon installed, migrations run, and the /horizon route responding for the same environment Nova is served from.
Where to look first:
vendor/mad-web/nova-horizon-link/src/ — the service provider and any Nova tool/menu-item class. This tells you how the link is registered (a Nova::menu entry vs. a Tool).config/horizon.php — specifically the path key. The link target depends on this value.app/Providers/HorizonServiceProvider.php — the gate() method that governs who can view Horizon at all.config/nova.php — Nova's own path and middleware, in case you mount Nova under a prefix.First use case: after install, refresh Nova. You should see a new sidebar entry (typically labelled Horizon or similar) that jumps straight to the Horizon dashboard. The day-to-day win is being able to go from "I'm in the admin panel" to "show me failed jobs / queue throughput" without typing a URL or remembering the environment's path.
If you don't see the link: clear config and route caches (php artisan config:clear route:clear) and re-check whether the package's provider is listed under bootstrap/providers.php (Laravel 11+) or discovered via vendor/composer/installed.json.
Plain sidebar link (default). The intended workflow is zero-config: install, refresh, click. Treat it as an ergonomic convenience rather than infrastructure — it should degrade gracefully if removed.
Pair it with Horizon's authorization gate. The most valuable integration is ensuring the Nova link is only visible to people who can actually load Horizon. If the package doesn't already defer to Horizon::check() / the viewHorizon gate, wrap or extend the menu registration so the link is hidden for non-authorized users rather than showing a 403 on click:
// app/Providers/NovaServiceProvider.php or a custom tool
Nova::menu(function (Menu $menu) {
if (Gate::allows('viewHorizon')) {
$menu->link('Horizon', fn () => '/horizon');
}
});
Environment-aware display. A common pattern is to only expose the link where Horizon is actually enabled. In local/dev without a queue worker running, Horizon is noise; in production it's the primary debugging surface. Gate on app()->environment() or on a config toggle so the same codebase behaves correctly across stacks.
Custom target path. If Horizon is mounted under a prefix (config('horizon.path') === 'admin/horizon') or Nova is served from a separate subdomain, the default /horizon link will be wrong. Resolve the URL from config instead of hardcoding:
fn () => url(config('horizon.path', 'horizon'))
Composing into a custom Nova tool. If you already ship an ops tool (a dashboard or a "System" tool), add the Horizon link as a menu item inside it instead of relying on the package's global sidebar entry, keeping all observability links in one place.
Incident workflow. The practical pattern is: Nova resource shows a failing job ID → click the Horizon link → land on the job detail → retry or delete. Documenting this two-click path for the team is more useful than the install itself.
horizon.path mismatch. The single most common cause of a broken link is a non-default Horizon path. Check config/horizon.php; the package may hardcode /horizon rather than resolving from config.viewHorizon). Don't assume a Nova login grants Horizon access; verify with a non-superadmin account.php artisan config:cache (or clear it). Menu items are registered at boot, so a stale cache will keep pointing at the old URL.https://example.com/admin/ and Nova is at /admin/nova, a root-relative /horizon link breaks. Prefer generating the link with url() / route() over a string literal.composer.json and be prepared to replace it with a five-line Nova::menu entry if it goes unmaintained or breaks against a Nova major upgrade. The functionality is trivial to reimplement.Menu service provider ordering and whether another package is calling Nova::menu in a way that shadows it; php artisan route:list | grep horizon confirms the target route actually exists in that environment.How can I help you explore Laravel packages today?