Skip to content
Get started
Get started

Quick Start: Your First Bun Job Queue in Minutes

guide · quickstart

Working queue in a minute.

Create a queue, add jobs, process them with a Worker, and wire up persistence. Everything on this page runs in a single Bun process with zero configuration.

bunqueue supports two deployment modes:

Embedded ModeTCP Server Mode
Best forSingle-process apps, serverlessMulti-process, microservices
SetupZero configRun bunqueue start first
Option neededembedded: trueNone (default)
PersistencedataPath option or DATA_PATH env var--data-path flag
ClientsBun only (in process)Node.js, Deno, Bun, Python, Cloudflare Workers

This guide covers Embedded Mode (most common). For TCP Server Mode, see Server Guide.

import { Queue } from 'bunqueue/client';
// Create a typed queue
interface EmailJob {
to: string;
subject: string;
body: string;
}
const emailQueue = new Queue<EmailJob>('emails', { embedded: true });
// Add a single job
const job = await emailQueue.add('send-email', {
to: 'user@example.com',
subject: 'Welcome!',
body: 'Thanks for signing up.'
});
console.log(`Job created: ${job.id}`);
// Add with options
await emailQueue.add('send-email', data, {
priority: 10, // Higher = processed first
delay: 5000, // Wait 5 seconds before processing
attempts: 3, // Retry up to 3 times
backoff: 1000, // Wait 1 second between retries
});
// Add multiple jobs (batch optimized)
await emailQueue.addBulk([
{ name: 'send-email', data: { to: 'a@test.com', subject: 'Hi', body: '...' } },
{ name: 'send-email', data: { to: 'b@test.com', subject: 'Hi', body: '...' } },
]);
import { Worker } from 'bunqueue/client';
const worker = new Worker<EmailJob>('emails', async (job) => {
console.log(`Processing: ${job.name}`);
// Update progress
await job.updateProgress(50, 'Sending email...');
// Do the work
await sendEmail(job.data);
// Log messages
await job.log('Email sent successfully');
// Return a result
return { sent: true, timestamp: Date.now() };
}, {
embedded: true, // Required for embedded mode
concurrency: 5, // Process 5 jobs in parallel
});
worker.on('completed', (job, result) => {
console.log(`Job ${job.id} completed:`, result);
});
worker.on('failed', (job, error) => {
console.error(`Job ${job.id} failed:`, error.message);
});
worker.on('progress', (job, progress) => {
console.log(`Job ${job.id} progress: ${progress}%`);
});
worker.on('active', (job) => {
console.log(`Job ${job.id} started`);
});
import { Queue, Worker, shutdownManager } from 'bunqueue/client';
interface EmailJob {
to: string;
subject: string;
}
// Producer - must have embedded: true
const queue = new Queue<EmailJob>('emails', { embedded: true });
// Add some jobs
await queue.add('welcome', { to: 'new@user.com', subject: 'Welcome!' });
await queue.add('newsletter', { to: 'sub@user.com', subject: 'News' });
// Consumer - must have embedded: true
const worker = new Worker<EmailJob>('emails', async (job) => {
console.log(`Sending ${job.data.subject} to ${job.data.to}`);
await job.updateProgress(100);
return { sent: true };
}, { embedded: true, concurrency: 3 });
worker.on('completed', (job) => {
console.log(`✓ ${job.id}`);
});
// Graceful shutdown
process.on('SIGINT', async () => {
await worker.close();
shutdownManager();
process.exit(0);
});

To persist jobs across restarts, pass dataPath in the constructor or set DATA_PATH before importing:

import { Queue, Worker } from 'bunqueue/client';
// Option 1: Pass dataPath directly (recommended)
const queue = new Queue('tasks', { embedded: true, dataPath: './data/bunqueue.db' });
const worker = new Worker('tasks', processor, { embedded: true, dataPath: './data/bunqueue.db' });
// Option 2: Environment variable
// DATA_PATH=./data/bunqueue.db bun run app.ts

Want less boilerplate? Bunqueue wraps Queue + Worker in a single object with routes, middleware, cron, and more:

import { Bunqueue } from 'bunqueue/client';
const app = new Bunqueue('notifications', {
embedded: true,
routes: {
'send-email': async (job) => {
await sendEmail(job.data.to);
return { sent: true };
},
'send-sms': async (job) => {
await sendSMS(job.data.to);
return { sent: true };
},
},
concurrency: 10,
});
// Middleware (wraps every job)
app.use(async (job, next) => {
const start = Date.now();
const result = await next();
console.log(`${job.name}: ${Date.now() - start}ms`);
return result;
});
// Cron jobs
await app.cron('daily-report', '0 9 * * *', { type: 'summary' });
// Add jobs
await app.add('send-email', { to: 'alice@example.com' });
// Graceful shutdown
await app.close();

Simple Mode also includes circuit breaker, batch processing, TTL, priority aging, deduplication, and debouncing. See Simple Mode guide for the full reference.

Everything you just built is visible in the web dashboard: queues, jobs and their states, the DLQ, cron schedules, workers, live activity, and a SQLite inspector. One command, no configuration:

Terminal window
bunx bunqueue-dashboard

Try the live demo without installing anything, or read the user guide.

bunqueue includes a native MCP server with 73 tools. AI agents can schedule tasks, manage pipelines, and monitor queues via natural language, no code needed.

Terminal window
# Claude Code, bunqueue-mcp is a binary bundled with bunqueue, so install it first
bun add bunqueue
bun add @modelcontextprotocol/sdk # optional peer dependency, required only for the MCP server
claude mcp add bunqueue -- bunx bunqueue-mcp
// Claude Desktop / Cursor / Windsurf: --package=bunqueue resolves the bundled binary, no install needed
{
"mcpServers": {
"bunqueue": {
"command": "bunx",
"args": ["--package=bunqueue", "bunqueue-mcp"]
}
}
}

Once connected, agents can add jobs, manage crons, retry failures, set rate limits, and monitor everything. See MCP Server guide for the full reference.

Need to orchestrate multi-step processes? bunqueue includes a built-in Workflow Engine with branching, saga compensation, and human-in-the-loop signals:

import { Workflow, Engine } from 'bunqueue/workflow';
const flow = new Workflow('order-pipeline')
.step('validate', async (ctx) => {
const { orderId } = ctx.input as { orderId: string };
return { orderId };
})
.step('charge', async (ctx) => {
return { txId: 'tx_123' };
}, {
compensate: async () => {
// Auto-rollback if a later step fails
await refundPayment('tx_123');
},
})
.waitFor('manager-approval') // Pauses until signal received
.step('ship', async (ctx) => {
const approval = ctx.signals['manager-approval'];
return { shipped: true };
});
const engine = new Engine({ embedded: true });
engine.register(flow);
const run = await engine.start('order-pipeline', { orderId: 'ORD-1' });
// Later, when the manager approves:
await engine.signal(run.id, 'manager-approval', { approved: true });

Built on top of bunqueue’s Queue and Worker, no new infrastructure. Workflow Engine guide for the full reference.