Skip to content
Get started
Get started
Server Mode: SQLite or PostgreSQL over TCP & HTTP
View Markdown
server · standalone

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).

Terminal window
# Defaults: TCP 6789, HTTP 6790, in-memory storage
bunqueue
# With persistence and custom ports
bunqueue start \
--tcp-port 6789 \
--http-port 6790 \
--data-path ./data/queue.db
# PostgreSQL: use a unique BUNQUEUE_BROKER_ID in every active process
BUNQUEUE_POSTGRES_URL='postgres://bunqueue:secret@postgres:5432/bunqueue' \
BUNQUEUE_POSTGRES_NAMESPACE=production \
BUNQUEUE_BROKER_ID=broker-a \
bunqueue start

Always 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.

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.

Without auth, anyone who can reach the port can control your queues. Set one or more tokens on the server:

Terminal window
AUTH_TOKENS=secret1,secret2 bunqueue start --data-path ./data/queue.db

Every client then needs a matching token in its connection options. More hardening tips in Security.

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:

VariableDefaultDescription
TCP_PORT6789TCP server port
HTTP_PORT6790HTTP server port
HOST0.0.0.0Bind address
BUNQUEUE_STORAGE_DRIVERinferredmemory, 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_NAMESPACEdefaultIsolates a bunqueue installation in one database
BUNQUEUE_BROKER_IDgeneratedUnique stable identity for each active PostgreSQL broker
AUTH_TOKENS(none)Comma-separated auth tokens
LOG_FORMATtextLog 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.

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:

Terminal window
docker run -d -p 6789:6789 -p 6790:6790 \
-v bunqueue-data:/app/data \
ghcr.io/egeominotti/bunqueue:2.9.4

PostgreSQL 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-alpine
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --production
COPY . .
EXPOSE 6789 6790
CMD ["bun", "run", "src/main.ts"]
Terminal window
docker build -t bunqueue .
docker run -p 6789:6789 -p 6790:6790 \
-v ./data:/app/data \
-e BUNQUEUE_DATA_PATH=/app/data/queue.db \
bunqueue

More deployment recipes (systemd, Kubernetes, Fly.io) in the deployment guide.

On SIGINT or SIGTERM the server:

  1. Stops accepting new connections
  2. Waits for active jobs to finish (30s timeout, configurable via SHUTDOWN_TIMEOUT_MS)
  3. Flushes SQLite writes or drains admitted PostgreSQL operations and maintenance
  4. Exits cleanly

AI agents can drive a running server through the bundled MCP server, which talks to bunqueue over TCP:

Terminal window
bunqueue start --data-path ./data/queue.db
# In another terminal
bun add bunqueue @modelcontextprotocol/sdk
claude mcp add bunqueue -- bunx --package=bunqueue bunqueue-mcp

Point 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.