bunqueue vs BullMQ: Reproducible 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.
Test Environment
Section titled “Test Environment”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 systemsconst 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.
Results Summary
Section titled “Results Summary”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
Push Throughput
Section titled “Push Throughput”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 producersconst 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); } }),);| Operation | bunqueue | BullMQ | Ratio |
|---|---|---|---|
| Single push | 52,756 ops/s | 56,736 ops/s | parity |
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 Push
Section titled “Bulk Push”Bulk operations show the biggest difference because bunqueue’s msgpack protocol batches efficiently:
// bunqueue - native bulkconst jobs = Array.from({ length: 100 }, (_, i) => ({ name: 'task', data: { id: i },}));await queue.addBulk(jobs);
// BullMQ - also supports addBulkawait queue.addBulk(jobs);| Scale | bunqueue | BullMQ | Ratio |
|---|---|---|---|
| 100 jobs/batch | 85,700 ops/s | 24,800 ops/s | 3.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.
Latency Under Load
Section titled “Latency Under Load”Throughput averages hide tail behavior, so the bench also records per-batch p99 latency on the bulk path:
| Metric | bunqueue | BullMQ | Ratio |
|---|---|---|---|
| Bulk push p99 (100 jobs) | 6.3ms | 11.1ms | 1.8x lower |
Embedded Mode (Bonus)
Section titled “Embedded Mode (Bonus)”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):
| Operation | Embedded peak |
|---|---|
| Single push | 241,825 ops/s |
| Bulk push | 630,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.
Run the Benchmarks Yourself
Section titled “Run the Benchmarks Yourself”All benchmarks are included in the repository:
git clone https://github.com/egeominotti/bunqueuecd 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 pageInstall Footprint
Section titled “Install Footprint”Throughput is one axis; what you ship is another. We measured bun add bunqueue in a clean project as of 2.8.1:
| Metric | bunqueue | Before 2.8.1 | Change |
|---|---|---|---|
node_modules size | 5.5 MB | 93 MB | −94% |
| packages installed | 7 | 117 | −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.
What the Numbers Mean
Section titled “What the Numbers Mean”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:
- 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
- Automatic batching - the client transparently coalesces concurrent add() and ack calls into bulk commands, so real workloads hit the fast path without code changes
- 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.