romanzipp/laravel-queue-monitor-nova
A Laravel Nova companion for romanzipp/Laravel-Queue-Monitor. It adds dashboard cards showing queue activity, job status, and failure metrics, letting teams monitor background jobs and spot problems directly from the Nova admin panel.
This is a thin Nova UI layer over romanzipp/Laravel-Queue-Monitor. It does not collect data — it only renders what the parent package has already stored. Install order matters.
# 1. Parent package first — this is what actually records jobs
composer require romanzipp/laravel-queue-monitor
php artisan vendor:publish --provider="RomanZipp\LaravelQueueMonitor\LaravelQueueMonitorServiceProvider"
php artisan migrate
# 2. Then the Nova tool
composer require romanzipp/laravel-queue-monitor-nova
Register the tool in app/Providers/NovaServiceProvider.php:
use RomanZipp\LaravelQueueMonitorNova\QueueMonitor; // confirm exact class from the README
public function tools()
{
return [
new QueueMonitor(),
];
}
Then clear caches (php artisan optimize:clear) and look for the new sidebar entry — that's your first-use case: watching a failed list fill up in staging without SSH-ing into the box and running queue:failed.
Where to look first, in this order:
config/queue-monitor.php in the parent package — enabled, connection, retention, and which job statuses get persisted.database/migrations/ — the table is the real contract the Nova tool reads.jobs table / failed_jobs table — cross-reference UUIDs before you trust the Nova view as a source of truth.If the sidebar entry never appears, you almost certainly forgot step 1 or the tool class name is wrong for your Nova version (see Gotchas).
Nova tools are visible to every authenticated Nova user unless you say otherwise. Wrap it:
public function tools()
{
return [
(new QueueMonitor())->canSee(function ($request) {
return $request->user()?->can('viewQueueMonitor');
}),
];
}
Pair that with a policy or a Gate::define('viewQueueMonitor', ...) so operators see it and support staff don't.
The single most common day-to-day question is "is ProcessStripeWebhook failing again?" Teach your team to filter by job class first, status second. Nova resource-style filters in this tool are only as good as the columns the parent package wrote — if job is null, collection was disabled upstream. Check queue-monitor.php for the option that controls whether the serialized payload/job name is stored; without it you get a table of anonymous timestamps.
Reading a stack trace in Nova is useful; acting on it is better. The pragmatic pattern is a Nova Action on the tool (or a sibling Resource) that dispatches the original job back onto its queue:
// app/Nova/Actions/RetryMonitoredJob.php
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $monitor) {
if ($monitor->status !== 'failed') {
continue;
}
dispatch(new RetryJob(...$monitor->payload));
}
return Action::message('Requeued');
}
Keep this outside the vendor tool — you don't want to patch a 3-star package to add business logic.
Because the data lives in a normal table, treat the Nova tool as a convenience view and the table as the API:
// Widget or scheduled command
$p95 = DB::table('queue_monitor')
->where('created_at', '>=', now()->subDay())
->where('status', 'succeeded')
->orderBy('duration') // adjust to actual column name
->value('duration');
This is the highest-leverage integration: the Nova tool gives humans a picture, DB::table('queue_monitor') gives you dashboards and alerts.
Enable the parent package's recording conditionally so production doesn't accumulate rows from noisy heartbeat jobs:
// config/queue-monitor.php
'enabled' => env('QUEUE_MONITOR_ENABLED', true),
Set QUEUE_MONITOR_ENABLED=false in local if you find yourself drowning in dev noise, but keep it true for at least one non-prod environment — you need a populated table to validate the Nova tool actually works before you roll it to prod.
This package is essentially unmaintained. 3 stars, 0 dependents, a score of 0.175. Treat it as a weekend convenience, not infrastructure. Before adopting:
composer.json in the package and comparing laravel/nova constraints against yours.fields() and gives you filters, lenses, and actions for free. Budget for this as a real possibility rather than a contingency.Nova licensing breaks CI. Nova isn't on Packagist; any test suite touching this tool needs the private repo credential configured. Don't put this tool in a shared test fixture — test the underlying queue_monitor table instead, which needs no license.
The Nova tool ships no migrations. Everything schema-related belongs to the parent package. If the tool renders an empty table and you've already run migrate, the parent package's migration was published to the wrong path or the connection in queue-monitor.php points somewhere your Nova app isn't reading.
Watch table growth. High-throughput queues will bloat queue_monitor fast. The parent package has pruning options — verify they're actually enabled and that the scheduler runs them. A 40GB queue monitor table is not a hypothetical; it's a rite of passage. Add an index on created_at and whatever status column you filter by.
Duration and timestamp semantics are easy to misread. "Running" rows have a null finish time; a crashed worker leaves a row stuck in running forever. Don't alert on status = 'running' duration without an upper bound — that's how you page yourself at 3am over a worker that was SIGKILLed during a deploy.
Debugging a blank tool: php artisan nova:list won't help, but php artisan route:list | grep -i queue will confirm the tool's routes registered. If routes exist but the UI 404s, it's almost always a stale compiled asset — php artisan nova:publish --force and a hard refresh.
Extension point reality check: the cleanest way to add columns or filters is not to fork this package but to register your own Nova Resource on the parent model and hide the tool. Given the dependency count here, that's not pessimism — it's the default plan.
How can I help you explore Laravel packages today?