Skip to content
Get started
Get started

bunqueue vs BullMQ: Reproducible Benchmarks

blog · benchmarks

bunqueue vs BullMQ, measured.

Performance claims without numbers are just marketing. These are reproducible benchmarks of bunqueue TCP against BullMQ with Redis on identical workloads, both over real network connections.

All numbers below come from one run, measured 2026-07-08 on an Apple M1 Max (32 GB) with Bun 1.3.14, against BullMQ 5.79.3 on Redis 8.8.0. 10,000 iterations, identical 100-byte payloads, and the full script is bench/comparison/run.ts in the repository:

// Identical payload for both systems
const PAYLOAD = { data: 'x'.repeat(100) };

Both use TCP connections (bunqueue TCP mode, BullMQ via Redis TCP). Embedded mode benchmarks are excluded since BullMQ has no equivalent.

3.5x Faster Bulk

85,700 vs 24,800 ops/sec - Bulk push (100 jobs per batch)

1.8x Lower p99

6.3ms vs 11.1ms - Bulk push p99 latency

Single Push: Parity

52,756 vs 56,736 ops/sec - BullMQ slightly ahead, effectively a tie

94% Smaller Install

5.5 MB vs 93 MB, 7 packages, no Redis server required

Single job push measures the raw ingestion speed. Both systems push 10,000 jobs from 32 concurrent producer loops:

// Same workload for both systems, 32 concurrent producers
const queue = new Queue('bench', {
connection: { host: 'localhost', port: 6789 }, // Redis :6379 for BullMQ
});
let issued = 0;
await Promise.all(
Array.from({ length: 32 }, async () => {
while (issued++ < 10_000) {
await queue.add('task', PAYLOAD);
}
}),
);
OperationbunqueueBullMQRatio
Single push52,756 ops/s56,736 ops/sparity

This one is a tie, and in this run BullMQ actually comes out slightly ahead. We are not going to spin that: Redis is extremely fast at single-command operations, and one job per request gives the wire protocol nothing to batch. If your workload is strictly one awaited add() at a time, bunqueue does not make it faster.

Bulk operations show the biggest difference because bunqueue’s msgpack protocol batches efficiently:

// bunqueue - native bulk
const jobs = Array.from({ length: 100 }, (_, i) => ({
name: 'task',
data: { id: i },
}));
await queue.addBulk(jobs);
// BullMQ - also supports addBulk
await queue.addBulk(jobs);
ScalebunqueueBullMQRatio
100 jobs/batch85,700 ops/s24,800 ops/s3.5x

The gap comes from encoding the entire batch in a single msgpack frame, while BullMQ issues multiple Redis commands (even with pipelines). This is the number that matters in practice, because bunqueue’s client auto-batches concurrent add() calls into bulk commands transparently, so real workloads hit this path without code changes.

Throughput averages hide tail behavior, so the bench also records per-batch p99 latency on the bulk path:

MetricbunqueueBullMQRatio
Bulk push p99 (100 jobs)6.3ms11.1ms1.8x lower

For single-process applications, embedded mode eliminates the network entirely. Peak throughput on the same machine, measured with bench/comprehensive.ts (median of 3 runs, best scale per operation):

OperationEmbedded peak
Single push241,825 ops/s
Bulk push630,568 ops/s

Bulk push peaks around 630K ops/sec at the 10K-job scale. There is no BullMQ column here because BullMQ has no embedded mode, which is exactly the point.

All benchmarks are included in the repository:

Terminal window
git clone https://github.com/egeominotti/bunqueue
cd bunqueue && bun install
bun run start & # bunqueue server (TCP mode)
redis-server --daemonize yes # Redis for the BullMQ half
bun run bench/comparison/run.ts # the numbers on this page

Throughput is one axis; what you ship is another. We measured bun add bunqueue in a clean project as of 2.8.1:

MetricbunqueueBefore 2.8.1Change
node_modules size5.5 MB93 MB−94%
packages installed7117−94%
cold install time~1.7 s~6 s~3.5× faster

The savings come from making @modelcontextprotocol/sdk an optional peer dependency (loaded lazily only by the bunqueue-mcp bin), dropping zod and the HTTP stack from the install path, and removing bun from peerDependencies. Queue and worker users now download just two runtime dependencies: croner and msgpackr.

bunqueue does not win everywhere, and we would rather say so than publish a chart that hides it. Single push is parity, because Redis is very fast and there is no protocol trick that beats it one command at a time. The wins are concentrated where batching applies:

  1. Bulk encoding - a 100-job batch travels as one msgpack frame instead of many Redis commands, 3.5x the throughput with 1.8x lower p99
  2. Automatic batching - the client transparently coalesces concurrent add() and ack calls into bulk commands, so real workloads hit the fast path without code changes
  3. Operational simplicity - no Redis server to deploy, monitor, back up, and scale

The tradeoff is clear: bunqueue is single-instance while BullMQ can leverage Redis clustering. For applications that fit on a single server (which is most of them), bunqueue delivers better batch throughput and lower tail latency with less operational complexity.