High-performance job queue for Bun. Built for AI agents and automation.
Zero external dependencies. MCP-native. TypeScript-first.
Documentation ·
Benchmarks ·
npm
Requirements
bunqueue is Bun-only (bun >= 1.3.9). The client, TCP transport and persistence
rely on Bun's runtime APIs (Bun.connect, Bun.file, Bun.hash, …) and ship as
ESM with extensionless specifiers, so they do not run under Node.js. Importing
the package from Node fails fast with a clear error pointing here rather than a
cryptic resolver crash. Install Bun from bun.sh and run with bun.
Quickstart
bun add bunqueue
import { Bunqueue } from 'bunqueue/client';
const app = new Bunqueue('emails', {
embedded: true,
processor: async (job) => {
console.log(`Sending to ${job.data.to}`);
return { sent: true };
},
});
await app.add('send', { to: '[email protected]' });
That's it. Queue + Worker in one object. No Redis, no config, no setup.
🪶 Featherweight install
bun add bunqueue pulls 7 packages and 5.4 MB — and that includes the binary
queue server, the CLI, and the MCP server. Most queue libraries pull a Redis
client and its dependency tree before you've queued a single job.
| Before (2.7.x) | Now | |
|---|
node_modules | 93 MB | 5.4 MB | −94% |
| packages | 117 | 7 | −110 |
| cold install | ~6 s | ~1.2 s | ~5× faster |
Just 2 runtime dependencies (croner + msgpackr). SQLite, S3, HTTP and
WebSocket are all Bun built-ins. The MCP SDK is an optional peer dependency —
queue users never download it. (details →)
Simple Mode
Simple Mode gives you a Queue and a Worker in a single object. Add jobs, process them, add middleware, schedule crons — all from one place.
Use Bunqueue when producer and consumer are in the same process. For distributed systems, use Queue + Worker separately. For AI agent workflows, use the MCP Server instead — agents control queues via natural language without writing code.
Architecture
new Bunqueue('emails', opts)
│
├── this.queue = new Queue('emails', ...)
├── this.worker = new Worker('emails', ...)
│
└── Subsystems (all optional):
├── RetryEngine ── jitter, fibonacci, exponential, custom
├── CircuitBreaker ── pauses worker after N failures
├── BatchAccumulator ── groups N jobs into one call
├── TriggerManager ── "on complete → create job B"
├── TtlChecker ── rejects expired jobs
├── PriorityAger ── boosts old jobs' priority
├── CancellationManager ── AbortController per job
└── DedupDebounceMerger ── deduplication & debounce
Processing pipeline per job:
Job → Circuit Breaker → TTL check → AbortController → Retry → Middleware → Processor
Routes
Route jobs to different handlers by name:
const app = new Bunqueue('notifications', {
embedded: true,
routes: {
'send-email': async (job) => {
await sendEmail(job.data.to);
return { channel: 'email' };
},
'send-sms': async (job) => {
await sendSMS(job.data.to);
return { channel: 'sms' };
},
},
});
await app.add('send-email', { to: 'alice' });
await app.add('send-sms', { to: 'bob' });
Note: Use one of processor, routes, or batch. Passing multiple or none throws an error.
Middleware
Wraps every job execution. Execution order is onion-style: mw1 → mw2 → processor → mw2 → mw1. When no middleware is added, zero overhead.