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 turn on persistence. On Bun everything runs embedded in a single process with zero configuration; from any other language, start the server once and connect.

Install the client for your runtime (see the SDK guide), save the snippet as a file, run it. On Bun the queue runs embedded in your process; in every other language, start the server once with bunx bunqueue start first.

import { Queue, Worker } from 'bunqueue/client';
// The queue: where you put jobs
const queue = new Queue('emails', { embedded: true });
// The worker: pulls jobs and runs your function on each one
const worker = new Worker('emails', async (job) => {
console.log(`Sending "${job.data.subject}" to ${job.data.to}`);
return { sent: true };
}, { embedded: true });
// Add a job
await queue.add('welcome', { to: 'user@example.com', subject: 'Welcome!' });

On Bun, embedded: true means the queue runs inside your process, no server needed. Every other client talks TCP to the server (bunx bunqueue start) and defaults to localhost:6789.

// Typed queue: job.data is type-checked
interface EmailJob {
to: string;
subject: string;
}
const emailQueue = new Queue<EmailJob>('emails', { embedded: true });
// Priority, delay, retries
await emailQueue.add('send-email', { to: 'a@test.com', subject: 'Hi' }, {
priority: 10, // Higher = processed first
delay: 5000, // Wait 5 seconds before processing
attempts: 3, // Retry up to 3 times if the processor throws
backoff: 1000, // Wait 1 second between retries (grows on each attempt)
});
// Many jobs at once (one optimized batch)
await emailQueue.addBulk([
{ name: 'send-email', data: { to: 'a@test.com', subject: 'Hi' } },
{ name: 'send-email', data: { to: 'b@test.com', subject: 'Hi' } },
]);

All options are in the Queue guide.

const worker = new Worker<EmailJob>('emails', async (job) => {
await job.updateProgress(50, 'Sending email...'); // Report progress
await sendEmail(job.data); // Do the work
await job.log('Email sent successfully'); // Attach a log line
return { sent: true, timestamp: Date.now() }; // Result, stored and queryable
}, {
embedded: true,
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}%`);
});

Rust and Elixir have no worker event emitter: use the structured telemetry callback for transport lifecycle and normal language control flow for per-job outcomes (see the SDK guide).

The full event list is in the Worker guide.

Without a data path, jobs live in memory and disappear on restart. Point bunqueue at a SQLite file to survive restarts:

// Option 1: dataPath option (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

dataPath applies to embedded (Bun) mode only. In server mode, persistence is configured on the server (bunx bunqueue start --data-path ./data/bunq.db); clients in every language need no changes.

import { shutdownManager } from 'bunqueue/client';
process.on('SIGINT', async () => {
await worker.close(); // Finish active jobs
shutdownManager(); // Flush pending writes, close SQLite
process.exit(0);
});

The Bun examples above run in a single process. When multiple services need to share one queue, run bunqueue as a standalone server instead:

Embedded modeServer mode
Best forSingle-process apps, serverlessMulti-process, microservices
Setupembedded: trueRun bunqueue start, drop the option
ClientsBun only (in process)Node.js, Deno, Bun, Python, PHP, Go, Rust, Elixir, Cloudflare Workers

See the Server guide. All six official client SDKs speak the same protocol against the same queues, see the SDK guide.

Less boilerplate. Bunqueue (Simple Mode) wraps Queue + Worker in one object with routes, middleware, and cron:

import { Bunqueue } from 'bunqueue/client';
const app = new Bunqueue('notifications', {
embedded: true,
routes: {
'send-email': async (job) => ({ sent: true }),
'send-sms': async (job) => ({ sent: true }),
},
concurrency: 10,
});
await app.add('send-email', { to: 'alice@example.com' });
await app.cron('daily-report', '0 9 * * *', { type: 'summary' });

Simple Mode is available in TypeScript and Python. In PHP, Go, Rust and Elixir, compose Queue and Worker directly.

See the Simple Mode guide.

Watch it live. The web dashboard shows queues, jobs, failures, crons, and workers. One command:

Terminal window
bunx bunqueue-dashboard

Try the live demo without installing anything.

Connect AI agents. bunqueue ships an MCP server with 73 tools, so agents like Claude can add jobs, manage crons, and monitor queues via natural language:

Terminal window
bun add bunqueue @modelcontextprotocol/sdk
claude mcp add bunqueue -- bunx bunqueue-mcp

Setup for Claude Desktop, Cursor, and Windsurf is in the MCP guide.

Orchestrate multi-step processes. The built-in workflow engine (Bun runtime) handles branching, parallel steps, rollback on failure, and human approvals:

import { Workflow, Engine } from 'bunqueue/workflow';
const flow = new Workflow('order')
.step('validate', async (ctx) => ({ ok: true }))
.step('charge', async (ctx) => ({ txId: 'tx_123' }))
.waitFor('manager-approval') // Pauses until you send a signal
.step('ship', async (ctx) => ({ shipped: true }));
const engine = new Engine({ embedded: true });
engine.register(flow);
await engine.start('order', { orderId: 'ORD-1' });

See the Workflow Engine guide.