DEV Community

Harsh
Harsh

Posted on

A practical guide to monitoring BullMQ queues with an agent-based approach that keeps Redis credentials inside your infrastructure.

devopscover_image: https://qcanary.dev/screenshots/dashboard-overview.png

Background jobs are the invisible backbone of your Node.js app. When they fail, they fail silently.

BullMQ is a great default for Node.js background jobs. It's fast, Redis-backed, and flexible enough for emails, reports, webhooks, and retry-heavy workflows. But once BullMQ is in production, a critical question shows up quickly:

How do you monitor queues without giving another service direct access to Redis?

Many hosted queue dashboards ask for your Redis URL. That works, but it comes with heavy security tradeoffs:

πŸ”‘ Credential exposure. Your production Redis URL gets stored by a third party.
πŸ”₯ Firewall changes. You have to open port 6379 or set up VPC peeringβ€”just for a dashboard.
πŸ“‹ Compliance surface. SOC 2 and zero-trust policies require a vendor risk assessment for that access.
πŸ—„οΈ Full data access. Redis has no row-level security. Once someone has the URL, they have everything.
Enter fullscreen mode Exit fullscreen mode

This post walks through an alternative: using BullMQ's built-in QueueEvents emitter to monitor queues without ever sharing a Redis credential.
The Problem With Direct Redis Monitoring

A traditional hosted queue monitor connects directly to Redis and inspects queue state. That's convenient for building dashboards, but uncomfortable for production systems.

Depending on your Redis permissions and deployment model, a Redis connection can expose far more than queue metrics β€” keys, job state, retry metadata, delayed jobs, and sometimes payload data. Even when a vendor is trustworthy, the credential becomes another secret to audit, rotate, and protect.

The better model is simple:

Keep Redis private. Send only the monitoring events you actually need.
Enter fullscreen mode Exit fullscreen mode

How BullMQ QueueEvents Changes the Game

BullMQ emits lifecycle events as jobs move through a queue. QueueEvents is the BullMQ primitive for subscribing to those changes inside your own process:

completed β†’ failed β†’ active β†’ waiting β†’ stalled β†’ delayed β†’ drained

Instead of polling Redis from a hosted service, you can subscribe from inside your runtime and translate these lifecycle events into monitoring records.

This eliminates:

 βœ… Inbound network access to Redis
 βœ… Redis credential sharing with vendors
 βœ… The need to inspect arbitrary Redis keys
 βœ… Firewall changes and VPC peering
Enter fullscreen mode Exit fullscreen mode

Monitoring data is buffered locally and sent over standard outbound HTTPS.
The Agent Pattern in Practice

Here's how it works in a real application. First, install the agent:
bash

npm install @qcanary/agent

Then attach it to your existing BullMQ queues:
typescript

import { Queue } from "bullmq";
import { QueueMonitor } from "@qcanary/agent";

const emailQueue = new Queue("email", {
connection: { host: "127.0.0.1", port: 6379 },
});

const monitor = new QueueMonitor({
apiKey: process.env.QCANARY_API_KEY,
queues: [emailQueue],
});

await monitor.start();

The agent does a few things under the hood:

Attaches to QueueEvents as a local subscriber β€” your Redis connection never leaves the process.
Buffers events into batches (100 events or 5 seconds, whichever comes first).
Ships only lightweight metadata over HTTPS: job ID, queue name, status, duration, error message.
Retries with exponential backoff on failure.
Drops oldest events if the buffer fills β€” monitoring should never slow down your workers.
Enter fullscreen mode Exit fullscreen mode

No job payloads. No Redis keys. No connection strings. Just events.
Setting Up Your First Alert

Dashboards are useful. Alerts are what make monitoring operational.

A good first alert is failure rate:
text

Queue: email
Condition: failure rate > 5%
Window: 10 minutes
Destination: Slack

One failed job may be noise. A sustained 5% failure rate over 10 minutes usually means something changed: a provider outage, expired credentials, a bad deploy, or a slow dependency.

Other useful alerts to configure:

 No activity β€” catch queues that silently stopped receiving work.
 Queue depth β€” catch growing backlogs before users feel the delay.
 Job duration β€” catch slow jobs and external dependency regressions.
Enter fullscreen mode Exit fullscreen mode

Comparing the Approaches
Aspect

Sharing Redis URL

Agent-Based (QueueEvents)
Credential exposure Redis URL stored by vendor None β€” Redis stays local
Network changes Open port 6379 or VPC peering None β€” outbound HTTPS only
Compliance impact Vendor risk assessment required None β€” no shared infrastructure
Data sent Direct Redis access (everything) Only lifecycle metadata
Payload visibility Vendor can read all job data Only metadata β€” payloads stay local
Setup time 10 minutes + network config 5 minutes, no network config

What to Monitor First

Start with high-impact queues:

 Email delivery
 Billing webhooks
 User notifications
 Scheduled reports
 Data imports and exports
 Third-party sync jobs
Enter fullscreen mode Exit fullscreen mode

Keep thresholds conservative at first. After a week of history, tune them based on normal production behavior.
What's Next for QCanary? (Expanding the Ecosystem)

Right now, QCanary is built specifically for BullMQ because of its clean event model. But we know the Node.js ecosystem is diverse.

We are actively expanding our agent to support the original Bull library, so legacy Node.js applications can secure their monitoring without migrating their queue infrastructure. Furthermore, we are laying the groundwork to support queue systems in other languages, bringing this same zero-trust, agent-based observability to Python, Go, and beyond.

If you're using Bull or another queue system, we'd love to hear from you!
When Sharing Redis Still Makes Sense

The agent pattern isn't always the right call:

 Local development β€” credential sharing doesn't matter on localhost.
 Full payload inspection needed β€” if you need to see job data in the dashboard, the agent can't help (it intentionally excludes payloads).
 Existing VPC peering β€” if the network boundary is already established, sharing Redis adds no new attack surface.
Enter fullscreen mode Exit fullscreen mode

For everyone else β€” production deployments with VPCs, compliance requirements, or zero-trust policies β€” the agent pattern eliminates an entire class of security risk.
Try It Free

QCanary provides real-time dashboards, Slack/email/webhook alerts, and job history for BullMQ β€” all without ever seeing your Redis URL.

Free tier: 1 project, 3 queues, 10K events/day, 3-day history.
Setup: Install the agent, attach to your queues, events start flowing.

β†’ qcanary.dev
β†’ qcanary.dev/docs

Found this useful? Follow me for more Node.js infrastructure content. Questions? Drop them in the comments below.

Top comments (0)