January 2, 2025

How to Monitor BullMQ Queues Without Writing Code

CorbinCorbin

You've got BullMQ running in production. Jobs are flowing through. Then one day, something breaks. A job fails silently. Or worse, jobs start piling up and you have no idea why.

The usual response? Add more console.log statements. Write a quick script to query Redis directly. Stare at raw JSON in the terminal at 2 AM.

There's a better way.

The Problem with BullMQ Visibility

BullMQ is fantastic for job processing. It handles retries, delays, priorities, and concurrency out of the box. But when it comes to observability, you're mostly on your own.

The built-in dashboard (Bull Board) helps, but it requires:

  • Adding another dependency to your project
  • Setting up an Express route
  • Deploying and securing another endpoint
  • Hoping it doesn't crash your app when you have millions of jobs

And even then, you can't easily:

  • See the actual Redis data structure
  • Query jobs by custom patterns
  • Bulk delete stuck jobs
  • Compare queue states across environments

What BullMQ Actually Stores in Redis

Before we fix the problem, let's understand what we're looking at. BullMQ stores everything in Redis using a consistent key pattern:

bull:<queue-name>:id          # Job counter
bull:<queue-name>:waiting     # List of waiting job IDs
bull:<queue-name>:active      # List of active job IDs
bull:<queue-name>:completed   # Set of completed job IDs
bull:<queue-name>:failed      # Set of failed job IDs
bull:<queue-name>:delayed     # Sorted set of delayed jobs
bull:<queue-name>:<job-id>    # Hash containing job data

Each job is stored as a Redis Hash with fields like data, opts, progress, returnvalue, failedReason, and timestamps.

The problem? This data is scattered across multiple keys and data types. Piecing it together manually is tedious.

Using Redimo's Pattern Monitor

Here's where pattern-based monitoring changes everything. Instead of writing code to aggregate queue data, you define a pattern and let the tool do the work.

Step 1: Create a Monitor Pattern

In Redimo, create a new Pattern Monitor with:

Pattern: bull:email-queue:*

This instantly captures all keys related to your email queue - waiting lists, active jobs, completed sets, and individual job hashes.

Step 2: View Queue Health at a Glance

The monitor dashboard shows you:

Metric What It Tells You
Total Keys Overall queue size
Type Distribution How many waiting vs active vs completed
Memory Usage Is this queue bloating your Redis?
TTL Status Are old jobs being cleaned up?

Step 3: Drill Into Failed Jobs

Filter by pattern bull:email-queue:failed or look for job hashes with failedReason fields. Redimo renders the job data as an expandable tree, so you can see exactly what failed and why.

No more:

const job = await queue.getJob(jobId);
console.log(JSON.stringify(job.data, null, 2));
console.log(job.failedReason);

Just click and see.

Common BullMQ Debugging Scenarios

Scenario 1: Jobs Stuck in Active State

Workers crashed mid-process, leaving jobs in active that will never complete.

CLI approach:

redis-cli LRANGE bull:myqueue:active 0 -1
# Get each job ID, then...
redis-cli HGETALL bull:myqueue:<job-id>
# Repeat for each stuck job

Redimo approach:

  1. Pattern Monitor on bull:myqueue:active
  2. See all stuck job IDs immediately
  3. Click any job to view full data
  4. Bulk select and delete, or move back to waiting

Scenario 2: Memory Growing Unexpectedly

Completed jobs aren't being cleaned up.

Check your cleanup settings:

const queue = new Queue('myqueue', {
  defaultJobOptions: {
    removeOnComplete: 1000,  // Keep last 1000
    removeOnFail: 5000,
  }
});

With Redimo:

  1. Monitor bull:myqueue:completed
  2. Check the count - is it in the millions?
  3. View memory usage per key type
  4. Bulk delete old completed jobs if needed

Scenario 3: Delayed Jobs Not Processing

Jobs scheduled for the future aren't running when expected.

Redimo approach:

  1. Monitor bull:myqueue:delayed
  2. This is a Sorted Set - scores are timestamps
  3. See exactly when each job is scheduled
  4. Spot jobs with timestamps in the past (something's wrong)

Bulk Operations Without Code

One of the biggest pain points with BullMQ debugging is bulk operations. Need to delete 10,000 failed jobs? In code:

const failed = await queue.getFailed(0, 10000);
await Promise.all(failed.map(job => job.remove()));

Hope your Node process doesn't run out of memory.

In Redimo:

  1. Filter to bull:myqueue:* with type failed
  2. Select all
  3. Delete with undo support

The deletion happens server-side. Your laptop doesn't need to hold 10,000 job objects in memory.

Multi-Queue Overview

Most real apps have multiple queues:

  • bull:email-queue:*
  • bull:payment-queue:*
  • bull:notification-queue:*
  • bull:analytics-queue:*

Create a monitor for each, or use a broader pattern like bull:*-queue:* to see everything. The tree view groups by queue name automatically.

Production Safety

When you're poking around production Redis, accidents happen. Redimo's Safe Mode prevents destructive operations:

  • Delete button requires confirmation
  • Production connections show warning indicators
  • All deletes are undoable (within a time window)

This matters when one wrong DEL command could wipe your entire job history.

Beyond BullMQ

The same pattern-based approach works for:

  • Sidekiq (Ruby): sidekiq:queue:*
  • Celery (Python): celery-task-meta-*
  • Agenda (Node): agendaJobs.*
  • Any custom queue: Whatever key pattern you're using

If it's in Redis, you can monitor it.

Getting Started

  1. Download Redimo (free tier available)
  2. Connect to your Redis instance
  3. Create a Pattern Monitor for bull:*
  4. Start seeing your queues clearly

No code changes. No new dependencies. No additional endpoints to secure.

Your BullMQ setup is already storing great data. You just need a better way to see it.

Ready for Download

Try Redimo Today

Pattern Monitor, CRUD operations, SSH Tunneling.
Everything you need to manage Redis at light speed.

macOS & Windows