




High-performance job queue for Bun. Memory or one-file SQLite; PostgreSQL 15–18 multi-broker when you scale.
DLQ, cron, SQLite S3 backups, and native MCP. Built for AI agents and automation. No Redis.
Documentation ·
Quick Start ·
Video course ·
Benchmarks ·
npm
Quickstart
Your first job in about a minute. On Bun the queue runs inside your app, with no
server to start:
bun add bunqueue
// jobs.ts
import { Queue, Worker } from 'bunqueue/client';
const queue = new Queue<{ to: string }>('emails', { embedded: true });
const worker = new Worker<{ to: string }>(
'emails',
async (job) => {
console.log('Processing:', job.data.to);
return { sent: true };
},
{ embedded: true }
);
worker.on('error', (error) => console.error(error));
await queue.add('welcome', { to: 'hello@example.com' });
bun jobs.ts
# Processing: hello@example.com
That's it: no Redis, no config, no setup. The worker keeps waiting for more jobs
until you stop it with Ctrl+C. The example keeps jobs in memory; pass
dataPath: './jobs.db' to the queue and the worker to persist them in one
SQLite file. msgpackr is the only runtime dependency; cron, SQLite, S3, HTTP and
WebSocket use Bun's built-ins.
Not on Bun? Run the server, connect from anywhere
Start the server with Docker (no Bun needed) or, if Bun is installed, with
bunx bunqueue start --host 127.0.0.1 --data-path ./bunqueue.db:
docker run -d --name bunqueue \
--restart unless-stopped \
-p 127.0.0.1:6789:6789 \
-p 127.0.0.1:6790:6790 \
-v bunqueue-data:/app/data \
egeominotti/bunqueue:alpine
curl --fail http://127.0.0.1:6790/health
Then, in another terminal, add a job and process it from Node.js:
npm install bunqueue-client
// jobs.mjs
import { Queue, Worker } from 'bunqueue-client';
const options = {
embedded: false,
connection: { host: '127.0.0.1', port: 6789 },
};
const queue = new Queue('emails', options);
const worker = new Worker(
'emails',
async (job) => {
console.log('Processing:', job.data.to);
return { sent: true };
},
options
);
worker.on('error', (error) => console.error(error));
await queue.add('welcome', { to: 'hello@example.com' });
node jobs.mjs
# Processing: hello@example.com
Deno, Python, PHP, Go, Rust and Elixir take the same three steps: see the
Quick Start for each language.
For multiple active brokers, the repository includes a topology pinned to
PostgreSQL 18.6:
POSTGRES_PASSWORD='replace-me' \
BUNQUEUE_POSTGRES_URL='postgres://bunqueue:replace-me@postgres:5432/bunqueue' \
docker compose -f docker-compose.postgres.yml up --build -d
It starts two brokers against one database/namespace. PostgreSQL is server-only;
embedded mode keeps using memory/SQLite. Supply both Compose values when the
password changes, percent-encoding reserved characters in the URL only. MySQL
is not supported. CI validates PostgreSQL 15, 16, 17, and the pinned/recommended
18.6 release. See the
storage guide.
Docker images
All four images run the same bunqueue server with the same queue features,
protocols, and persistence options. The difference is the Linux base and the
tools available inside the container.
Starting with 2.9.5, completed releases publish to both Docker Hub
(egeominotti/bunqueue) and GHCR (ghcr.io/egeominotti/bunqueue), with matching
version, latest, and variant tags. Build references are kept on GHCR only.
Each image supports linux/amd64 and linux/arm64; Docker selects the matching
architecture automatically. You choose the distribution, not the CPU tag.
Pin a version or digest for reproducible deployments. Confirm a tag exists with
docker buildx imagetools inspect egeominotti/bunqueue:<tag> before using it.
Choose a Linux distribution with the same tags on either registry:
| Variant | Version tag | Moving tag | Runtime base |
|---|
| Alpine (default) | 2.9.21-alpine | alpine, latest | Alpine 3.22, musl |
| Debian | 2.9.21-debian | debian | Debian 13 |
| Debian slim | 2.9.21-slim | slim | Debian 13 slim |
| Distroless | 2.9.21-distroless | distroless | Debian 13, no shell or package manager |
Which variant should I use?
- Alpine is the default for a compact deployment. It uses musl and includes
a shell and the
apk package manager. Choose it when you do not need Debian
tooling or glibc compatibility for additional software.
- Debian uses glibc and the standard Debian base, with a shell and
apt.
Choose it when familiar Debian tools and a fuller base matter more than image
size. Additional troubleshooting tools may still need to be installed.
- Debian slim keeps glibc, a shell, and
apt in a reduced Debian base.
Choose it when you want Debian compatibility with fewer bundled utilities.
- Distroless contains the compiled server and its required runtime libraries,
without a shell or package manager. Choose it when you operate through logs,
health checks, and external debugging tools;
docker exec ... sh is unavailable.
The distribution does not select a faster queue engine or unlock extra features.
All images run as a non-root user; having a package manager does not grant that
user permission to install packages at runtime.
Try a specific variant by changing only the image tag:
docker run -d --name bunqueue --restart unless-stopped \
-p 127.0.0.1:6789:6789 -p 127.0.0.1:6790:6790 \
-v bunqueue-data:/app/data \
egeominotti/bunqueue:2.9.21-alpine
curl --fail http://127.0.0.1:6790/health
This example exposes the APIs only on your machine. Replace 2.9.21-alpine with
2.9.21-debian, 2.9.21-slim, or 2.9.21-distroless to choose another base.
Moving tags such as alpine follow newer releases; version tags identify a
release, while a digest pins the exact image even across base-image rebuilds.
Every variant supports both architectures, runs as UID/GID 1001:1001, and
stores SQLite data in /app/data. Unsuffixed tags such as 2.9.21 stay on Alpine.
Production images contain the compiled server and required system libraries;
development dependencies and a separate Bun installation stay out of the image.
The built-in health check uses /app/bunqueue healthcheck, including on distroless.
See the deployment guide for custom probes.
Standalone executables and network clients
Prefer a standalone executable? GitHub releases
include the Bun runtime, so no Bun or Node.js installation is needed. From 2.9.5,
the eight downloads are:
| System | Architecture | Archive |
|---|
| Linux (glibc) | x64 | bunqueue-linux-x64.tar.gz |
| Linux (glibc) | arm64 | bunqueue-linux-arm64.tar.gz |
| Linux (musl / Alpine) | x64 | bunqueue-linux-x64-musl.tar.gz |
| Linux (musl / Alpine) | arm64 | bunqueue-linux-arm64-musl.tar.gz |
| macOS | x64 / Intel | bunqueue-darwin-x64.tar.gz |
| macOS | arm64 / Apple Silicon | bunqueue-darwin-arm64.tar.gz |
| Windows | x64 | bunqueue-windows-x64.zip |
| Windows | arm64 | bunqueue-windows-arm64.zip |
Extract the archive for your operating system and architecture, and verify it
against the release's SHA256SUMS. See the
installation guide.
Then produce and process from the language you already use:
npm install bunqueue-client # Node.js ≥ 20, Deno ≥ 2, Bun, Cloudflare Workers
import { Queue, Worker } from 'bunqueue-client';
const queue = new Queue('emails'); // localhost:6789 by default
await queue.add('welcome', { to: 'user@example.com' });
new Worker('emails', async (job) => ({ sent: true }), { concurrency: 10 });
Python, PHP, Go, Rust and Elixir clients speak the same protocol — see
One Queue, Any Language.
Only the server and embedded mode are Bun-only (bun >= 1.4.0,
bun.sh); producers and workers can run anywhere.
Quick Start guide →
Learn with bunqueue Academy
A free video course on YouTube, from your first queue to production. Each
episode follows a guide on bunqueue.dev, with chapters and English subtitles.
Coming next: 07 Workflow Engine · 08 SDKs, CLI & MCP · 09 Production · 10
Framework Integrations.
Full playlist → ·
All episodes on bunqueue.dev →
Why bunqueue?
| Library | Requires | AI-native |
|---|
| BullMQ | Redis | No |
| Agenda | MongoDB | No |
| pg-boss | PostgreSQL | No |
| bunqueue | Nothing (memory/SQLite) · PostgreSQL optional | Yes |