# Server Mode: SQLite or PostgreSQL over TCP & HTTP

Deploy bunqueue as one SQLite broker or a PostgreSQL 15–18 broker fleet, with TCP/HTTP APIs, token auth, Docker, and graceful shutdown.

Canonical: https://bunqueue.dev/guide/server/

---

<div class="bq-wrap bq-hero">
  <span class="bq-eyebrow">server · standalone</span>
  <h1 class="bq-hero-h1 bq-bench-h1">One server API. <em>One or many brokers.</em></h1>
  <p class="bq-hero-sub">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.</p>
</div>

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

```bash
# 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](/guide/databases/).

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.

The server validates every setting before it opens a port. A flag, config-file
key or environment variable with a value the server cannot use, such as
`--tcp-port 1e4`, `timeouts.stats: 0` or `STATS_INTERVAL_MS=1e12` (which earlier
releases misread as port 1 and 1 ms), stops startup with an error that names it.
Values that earlier releases accepted keep working: forms such as `6789.5` or
`5000ms` are read as they were, and a value they replaced with a default (for
example `--tcp-port abc`, which starts on 6789) is logged as a warning and keeps
that default. See [Configuration File](/guide/configuration/#validation) and
[Environment Variables](/guide/env-vars/).

## Connect from your app

Drop the `embedded` option and clients connect to `localhost:6789` automatically:

```typescript
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:

```typescript
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](/guide/sdks/) for TypeScript on Node.js/Deno/Cloudflare Workers, Python, PHP, Go, Rust, or Elixir.

## Add authentication

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

```bash
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](/security/).

## Configure it

The recommended way is a typed `bunqueue.config.ts` file in your project root, auto-discovered by `bunqueue start`:

```typescript
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](/guide/configuration/) 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](/guide/env-vars/).

## 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:

```bash
docker run -d -p 6789:6789 -p 6790:6790 \
  -v bunqueue-data:/app/data \
  ghcr.io/egeominotti/bunqueue:2.9.14
```

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:

```dockerfile
FROM oven/bun:1.4.3-alpine
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --production
COPY . .
EXPOSE 6789 6790
CMD ["bun", "run", "src/main.ts"]
```

```bash
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](/guide/deployment/).

## Graceful shutdown

On `SIGINT` or `SIGTERM` the server:

1. Stops accepting new connections and stops handing out jobs (a pull delivers nothing; one with a timeout waits it out before answering), while open connections keep working so workers can still acknowledge, fail and heartbeat the jobs they hold
2. Waits for active jobs to finish, and moves on as soon as none is active (30s timeout, configurable via `SHUTDOWN_TIMEOUT_MS`; a job still active at the timeout is recovered by the next start as interrupted work)
3. Closes the TCP and HTTP servers
4. Flushes SQLite writes or drains admitted PostgreSQL operations and maintenance
5. Exits cleanly

## Connect AI agents (MCP)

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

```bash
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 75 tools to add jobs, manage queues, schedule crons, and monitor everything. Full setup, including Claude Desktop, Cursor, and Windsurf config, in the [MCP guide](/guide/mcp/).

:::tip[Related Guides]

- [Environment Variables](/guide/env-vars/), all server configuration options
- [CLI Commands](/guide/cli/), manage the server from the terminal
- [Security Best Practices](/security/), secure your deployment
- [Monitoring & Prometheus Metrics](/guide/monitoring/), watch server health
  :::