Cloud Parts

BullMQ

The Node.js job queue on Redis, Bull’s successor, with ports to other languages and, since v6, a Postgres backend.

What it is
Message queues
Type
Open source
Made by
Taskforce.sh (BullForce Labs AB)
License
MIT (BullMQ Pro is commercial)
Facts checked
6 October 2026

What it is

BullMQ is a library, not a server. A Queue object in your app adds jobs, and Worker objects in your own processes pick them up; all the state lives in Redis 6.2 or newer, or a compatible server. A job moves between states (waiting, prioritized, delayed, active, completed, failed), and each change runs as a Lua script inside Redis, so it stays atomic with many workers. A worker holds a lock on each active job and keeps renewing it. If the lock lapses, because the process died or blocked the event loop too long, the job is “stalled” and goes back to waiting, or to failed after too many stalls. On top come retries with fixed or exponential backoff, delays, priorities, rate limits, deduplication, cron-style job schedulers and parent and child jobs (“flows”) across queues.

The core is MIT-licensed and developed by Taskforce.sh; Manuel Astudillo, who wrote the earlier Bull library, started it. It’s native to Node.js and Bun, with ports for Python, Elixir, .NET and Rust that share queues with Node.js, and a PHP client that can only add jobs. BullMQ 6 (July 2026) made storage pluggable and added a PostgreSQL backend with the same API, which its docs put at roughly 1.5 to 2 times fewer processed jobs a second than Redis. BullMQ Pro adds groups, batches and observables for Node.js and Bun under a commercial licence: $1,395 a year or $139 a month per deployment for organizations with fewer than 100 employees, and custom pricing above that (checked October 2026).

Why teams pick it

  • A mature feature set: retries with backoff, delays, priorities, rate limiting, deduplication, job schedulers and parent and child flows
  • Runs on Redis, Valkey and Dragonfly (all three tested in CI), managed services such as ElastiCache and MemoryDB, and since v6 on PostgreSQL
  • Queues are shared across languages: Node.js, Python, Elixir, .NET and Rust workers, plus PHP producers
  • Fast: its site cites over 250,000 jobs a second in benchmarks on Dragonfly
  • Sandboxed processors run CPU-heavy jobs in separate processes, and graceful shutdown and stalled-job recovery are built in

Watch out for

  • Redis must use maxmemory-policy noeviction; a cache-style eviction policy breaks queues, and ElastiCache Serverless can’t be set that way
  • Completed and failed jobs stay in Redis by default; without removeOnComplete and removeOnFail, memory keeps filling
  • A handler that blocks Node’s event loop can’t renew its lock, so the job stalls and runs again on another worker
  • Groups (per-tenant fairness), batches and observables need the paid Pro edition, for Node.js and Bun only
  • Ports aren’t equal: PHP can only add jobs and Python was introduced as experimental; v6 removed legacy repeatable jobs and Redis internals, so upgrading needs code changes

Alternatives

What all message queues have in common →

How it fits with other technologies

Add a job with retries, process it, and log failures · ts

import { Queue, Worker } from 'bullmq';
import IORedis from 'ioredis';

const connection = new IORedis({ maxRetriesPerRequest: null });

const thumbnails = new Queue('thumbnails', { connection });
await thumbnails.add(
  'resize',
  { key: 'uploads/123.jpg', width: 320 },
  { attempts: 5, backoff: { type: 'exponential', delay: 1000 } },
);

const worker = new Worker(
  'thumbnails',
  async job => {
    await resize(job.data.key, job.data.width);
    return { done: true };
  },
  { connection, concurrency: 10, removeOnComplete: { count: 1000 } },
);

worker.on('failed', (job, err) => console.error(job?.id, err.message));

Where BullMQ can keep its queues#

The API is the same everywhere; what changes is the setting you have to get right.

BackendStatusWhat to watch
Redis 6.2+Default backendSet maxmemory-policy to noeviction
ValkeyTested in CI against Valkey 8Also works through the Valkey Glide client adapter
DragonflyTested in CI; listed as officially supportedWrap queue names in {} so each queue gets its own thread
Amazon ElastiCacheDocumentedNode-based clusters only; Serverless uses an incompatible eviction policy
Amazon MemoryDBDocumentedCluster mode only, so put hash tags in queue names
PostgreSQL 13+Since v6.0 (July 2026)LISTEN/NOTIFY replaces blocking pops; about 1.5 to 2 times fewer jobs a second than Redis

Bull, BullMQ and BullMQ Pro#

BullBullMQBullMQ Pro
StatusMaintenance mode: bug fixes onlyActively developedActively developed
Licence and priceMIT, freeMIT, freeCommercial: $1,395 a year or $139 a month per deployment (under 100 employees); custom above
LanguagesNode.jsNode.js and Bun, plus Python, Elixir, .NET, Rust and PHP clientsNode.js and Bun
Parent and child flowsNoYesYes
Groups, batches, observablesNoNoYes
  • A deployment is one operational environment, such as a Kubernetes cluster, server or VM, connecting to one or more Redis instances. Prices from bullmq.io, checked 2026-10-06.

See it in a real stack

No architecture uses BullMQ yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2013–2026

In June 2013 Manuel Astudillo of OptimalBits started Bull, a job queue for Node.js that kept its state in Redis and leaned on Redis’s atomic operations. It sat alongside libraries such as Kue and Bee, and Redis’s own guide to queues later picked Bull as its Node.js example.

In December 2018 Astudillo began BullMQ, a rewrite in TypeScript, under the taskforcesh organization on GitHub; 1.0 shipped in October 2019. Bull went into maintenance mode, fixing only bugs. The business around it is Taskforce.sh, which sells a hosted dashboard for queues and BullMQ Pro, a commercial edition with groups, batches and observables, while the core stays MIT.

BullMQ then stopped being only a Node.js library. A Python package appeared in 2023, Elixir and PHP clients in December 2025, and Rust and .NET in mid-2026, all reading and writing the same queues. BullMQ 6, in July 2026, hid Redis behind a backend interface and added PostgreSQL, so the same code can run on a database many teams already operate.

Why it’s built this way

Lua scripts for every state change
Moving a job between states touches several Redis keys. Running each move as a script inside Redis makes it atomic without locks in the application, so many workers can share one queue safely.
A library, not a server
Queues and workers are objects in your own processes, and Redis holds the state, so there’s nothing extra to deploy. Other platforms can reach queues through the separate BullMQ Proxy.
Renewable locks on active jobs
A worker that dies can’t release its jobs, so each lock must be renewed periodically. A lapsed lock marks the job stalled and returns it to the queue.
An MIT core with a paid Pro edition
Pro features such as groups and batches, plus support, fund development, while the main library stays free with no limits on workers or concurrency.

Timeline

  1. 2013

    Manuel Astudillo starts Bull at OptimalBits

  2. 2018

    BullMQ, a TypeScript rewrite, begins in December

  3. 2019

    BullMQ 1.0 released in October

  4. 2023

    First Python package (0.1) released

  5. 2025

    Elixir and PHP clients reach first releases

  6. 2026

    BullMQ 6 adds pluggable backends and PostgreSQL

Sources20 · facts checked 6 October 2026

Improve this page on GitHub