- Docs
- Run in Production
- Running the Server
One server API. One or many brokers.
Run bunqueue as a standalone service so multiple apps can share a queue. Keep one memory/SQLite broker, or point several brokers at PostgreSQL 15–18; producers and workers use the same TCP API either way.
Embedded mode ties the queue to one Bun process. Server mode runs bunqueue standalone: your API adds jobs from one service, workers process them from another, and all six external SDKs join over the wire. Every broker listens on two ports: 6789 (TCP, the fast binary protocol clients use) and 6790 (HTTP, REST API and metrics).
Start the server
Section titled “Start the server”# Defaults: TCP 6789, HTTP 6790, in-memory storagebunqueue
# With persistence and custom portsbunqueue start \ --tcp-port 6789 \ --http-port 6790 \ --data-path ./data/queue.db
# PostgreSQL: use a unique BUNQUEUE_BROKER_ID in every active processBUNQUEUE_POSTGRES_URL='postgres://bunqueue:secret@postgres:5432/bunqueue' \BUNQUEUE_POSTGRES_NAMESPACE=production \BUNQUEUE_BROKER_ID=broker-a \bunqueue startAlways select durable storage in production: set a SQLite --data-path, or configure BUNQUEUE_POSTGRES_URL for the server-only multi-broker backend. Without either, jobs live in memory and are lost on restart. PostgreSQL 15–18 is tested in CI and 18.6 is recommended; see Storage backends.
For bounded completed-job history on SQLite, add
--completed-retention-ms <age> (or the equivalent config/environment
setting). --max-completed-jobs only sizes the in-memory hot window and does
not reclaim disk space.
Connect from your app
Section titled “Connect from your app”Drop the embedded option and clients connect to localhost:6789 automatically:
import { Queue, Worker } from 'bunqueue/client';
const queue = new Queue('tasks');const worker = new Worker('tasks', async (job) => { console.log('Processing:', job.data); return { success: true };});
await queue.add('my-job', { foo: 'bar' });For a remote server, pass a connection:
const queue = new Queue('tasks', { connection: { host: '192.168.1.100', port: 6789, token: 'my-secret-token', // Required if the server sets AUTH_TOKENS },});Not on Bun? Use the client SDKs for TypeScript on Node.js/Deno/Cloudflare Workers, Python, PHP, Go, Rust, or Elixir.
Add authentication
Section titled “Add authentication”Without auth, anyone who can reach the port can control your queues. Set one or more tokens on the server:
AUTH_TOKENS=secret1,secret2 bunqueue start --data-path ./data/queue.dbEvery client then needs a matching token in its connection options. More hardening tips in Security.
Configure it
Section titled “Configure it”The recommended way is a typed bunqueue.config.ts file in your project root, auto-discovered by bunqueue start:
import { defineConfig } from 'bunqueue';
export default defineConfig({ server: { tcpPort: 6789, httpPort: 6790 }, auth: { tokens: ['my-secret-token'] }, storage: { dataPath: './data/queue.db' },});See Configuration File for every option. Environment variables work too, as a fallback:
| Variable | Default | Description |
|---|---|---|
TCP_PORT | 6789 | TCP server port |
HTTP_PORT | 6790 | HTTP server port |
HOST | 0.0.0.0 | Bind address |
BUNQUEUE_STORAGE_DRIVER | inferred | memory, sqlite, or postgres |
BUNQUEUE_DATA_PATH | (memory) | SQLite database path |
BUNQUEUE_POSTGRES_URL | (none) | PostgreSQL URL; selects postgres when no driver is set |
BUNQUEUE_POSTGRES_NAMESPACE | default | Isolates a bunqueue installation in one database |
BUNQUEUE_BROKER_ID | generated | Unique stable identity for each active PostgreSQL broker |
AUTH_TOKENS | (none) | Comma-separated auth tokens |
LOG_FORMAT | text | Log format (text / json) |
Priority when the same option is set in more than one place: CLI flags > config file > environment variables > defaults. Full list in Environment Variables.
Run it in Docker
Section titled “Run it in Docker”Every completed release publishes the multi-arch image with the exact version
tag alongside latest and the distribution variants. Pin the version
tag when the server and client must move together:
docker run -d -p 6789:6789 -p 6790:6790 \ -v bunqueue-data:/app/data \ ghcr.io/egeominotti/bunqueue:2.9.4PostgreSQL storage needs a 2.9 image or newer: a 2.8.x image ignores the
variables above and starts in memory or SQLite mode without an error. Confirm
the tag you pin exists — a release whose pipeline did not complete pushes no
image, and 2.9.0 is one such gap. docker buildx imagetools inspect ghcr.io/egeominotti/bunqueue:<tag> prints the digest a rollout should pin.
To build an application-specific image instead:
FROM oven/bun:1.4.2-alpineWORKDIR /appCOPY package.json bun.lock* ./RUN bun install --productionCOPY . .EXPOSE 6789 6790CMD ["bun", "run", "src/main.ts"]docker build -t bunqueue .docker run -p 6789:6789 -p 6790:6790 \ -v ./data:/app/data \ -e BUNQUEUE_DATA_PATH=/app/data/queue.db \ bunqueueMore deployment recipes (systemd, Kubernetes, Fly.io) in the deployment guide.
Graceful shutdown
Section titled “Graceful shutdown”On SIGINT or SIGTERM the server:
- Stops accepting new connections
- Waits for active jobs to finish (30s timeout, configurable via
SHUTDOWN_TIMEOUT_MS) - Flushes SQLite writes or drains admitted PostgreSQL operations and maintenance
- Exits cleanly
Connect AI agents (MCP)
Section titled “Connect AI agents (MCP)”AI agents can drive a running server through the bundled MCP server, which talks to bunqueue over TCP:
bunqueue start --data-path ./data/queue.db
# In another terminalbun add bunqueue @modelcontextprotocol/sdkclaude mcp add bunqueue -- bunx --package=bunqueue bunqueue-mcpPoint the MCP server at your instance with BUNQUEUE_MODE=tcp, BUNQUEUE_HOST, BUNQUEUE_PORT, and BUNQUEUE_TOKEN (when auth is on). Agents get 73 tools to add jobs, manage queues, schedule crons, and monitor everything. Full setup, including Claude Desktop, Cursor, and Windsurf config, in the MCP guide.