If youβve ever needed to handle background jobs or async tasks in Node.js, youβve likely run into tools like Bull, Agenda, or Kue. But these days, BullMQ is the go-to solution for serious queue management in modern Node environments β built on top of Redis with support for repeatable jobs, concurrency, retries, events, and more.
This post walks through how to set up BullMQ, build a basic job queue, and structure your system for production. And along the way, weβll share a few lessons learned about scaling queues safely β especially when things go wrong.
π¨ TL;DR
- BullMQ is a powerful Redis-based job queue library for Node.js
- You can create queues, add jobs, and run workers easily
- Job options let you manage retries, delays, and cleanup
- In production, observability is key β monitor workers, failures, and Redis
- Upqueue.io can help make that easy, but even logs and alerts go a long way
π What Is BullMQ, and Why Use It?
BullMQ is a message queue library for Node.js that uses Redis to persist jobs. It’s ideal for handling:
- Long-running tasks (e.g., sending emails, video processing)
- Scheduled or recurring jobs
- Offloading CPU-heavy work from your main thread
Unlike its predecessor Bull, BullMQ is built using TypeScript and offers a more modular and powerful API.
π§± Installation & Setup
Letβs create a basic queue system using BullMQ.
1. πΎ Installation
npm install bullmq ioredis
2. π Connect to Redis
// redis-conn.ts
import { RedisOptions } from 'ioredis';
export const connection: RedisOptions = {
host: 'localhost',
port: 6379,
};
3. π§Ύ Define a Queue
// queues.ts
import { Queue } from 'bullmq';
import { connection } from './redis-conn';
export const emailQueue = new Queue('emailQueue', { connection });
4. βοΈ Add Jobs to the Queue
// producer.ts
import { emailQueue } from './queues';
emailQueue.add('sendWelcomeEmail', {
userId: '123',
email: 'user@example.com',
});
5. π·ββοΈ Create a Worker
// worker.ts
import { Worker } from 'bullmq';
import { connection } from './redis-conn';
const worker = new Worker('emailQueue', async job => {
const { userId, email } = job.data;
await sendEmail(email);
}, { connection });
π Job Options: Retries, Delay, Cleanup
BullMQ supports tons of job-level options:
emailQueue.add('sendWelcomeEmail', {
userId: '123',
email: 'user@example.com',
}, {
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
removeOnComplete: true,
});
π§ͺ Testing Locally
Start your Redis server and run:
node producer.ts
node worker.ts
Youβll now have a queue running β but what happens when things go wrong?
π Observability & Troubleshooting
In production, issues like these are common:
- Jobs fail silently
- Queues stop processing (due to missing workers)
- Redis connections drop unexpectedly
To prevent 3am fire drills, we highly recommend implementing:
- Failed job monitoring
- Stalled job detection
- Missing worker alerts
- Queue backlog warnings
Some devs roll their own scripts or Prometheus integrations. We built Upqueue.io as a quick plug-and-play dashboard that offers these exact insights without needing to dig through logs or write Redis scripts.
π‘ Production Tips
β
Use removeOnComplete: true
to avoid memory bloat
β
Monitor failed jobs with retry limits
β
Gracefully shut down workers using worker.close()
β
Namespace your queues if you use multi-tenant architecture
β
Set alerts for Redis max memory or dropped connections
π Wrapping Up
BullMQ is a robust and flexible solution for queue management in Node.js apps. Whether you’re processing uploads, triggering workflows, or building AI pipelines, it’s a reliable backbone for async work.
But donβt just focus on adding jobs β watch how they behave, track what fails, and build in resilience.
Itβll save you hours. Maybe even a weekend.