Durabull Documentation

Docker Image and Compose

Run the official image, preserve data, configure Compose, and upgrade a self-hosted deployment.

The official image serves the web app, HTTP API, and MCP on one port. For a disposable localhost trial, follow Installation. This guide covers a persistent setup.

Choose an image

Images are published as ghcr.io/durabullhq/durabull:<version>. Use latest for evaluation; pin a published version tag or image digest for production. Find version tags in the container registry.

Start the maintained Compose stack

The repository includes tooling/docker/docker-compose.self-hosted.yaml. It starts Durabull and Redis, enables authentication, and stores both services' data in named volumes. Redis is accessible only on the internal Docker network. The default database is PGlite.

From the repository root, generate two separate secrets:

openssl rand -hex 32
openssl rand -hex 32

Create a private .env.self-hosted file and paste the generated values:

BETTER_AUTH_SECRET=<first-generated-value>
DURABULL_REDIS_URL_ENCRYPTION_KEY=<second-generated-value>
APP_BASE_URL=http://localhost:3000
VITE_PUBLIC_APP_URL=http://localhost:3000
DURABULL_APP_PORT=127.0.0.1:3000
DURABULL_AUTHLESS=false

The placeholders must be replaced; dotenv files do not execute shell expressions. Keep this file outside version control, preserve the secrets across restarts, and back them up securely. DURABULL_APP_PORT=127.0.0.1:3000 binds the shipped port mapping to localhost for initial setup.

docker compose --env-file .env.self-hosted \
  -f tooling/docker/docker-compose.self-hosted.yaml up -d

Open http://localhost:3000, create an account, and set up an organization. The default MAIN connection points at the empty Redis service in this stack. Connect your BullMQ application to that Redis instance, or set DURABULL_REDIS_URL_MAIN to an existing Redis URL reachable from the container and recreate the app service.

Environment-managed connections are the Compose default. To manage them in the UI, set DURABULL_ENV_CONNECTIONS=false. Redis URLs are encrypted in either mode, so keep the encryption key.

Configure a public deployment

Set APP_BASE_URL and VITE_PUBLIC_APP_URL to the public HTTPS origin. Keep DURABULL_AUTHLESS=false, put Durabull behind a reverse proxy, and review Security and Hardening.

The reverse proxy must route /, /api/*, /mcp, and /.well-known/* to the same app and preserve the public Host. Set TRUST_PROXY=true in the container only when the proxy controls and replaces forwarding headers. The shipped Compose file does not forward this variable automatically.

Add settings not included in the Compose file

Compose's --env-file supplies values for ${...} substitutions. It does not pass every variable in the file into the container. The maintained stack forwards only the variables listed in its environment block.

For PostgreSQL, email, Linear, custom queue prefixes, or other additional settings, add them to that block or create an override file. For example, compose.override.yaml at the repository root:

services:
  durabull:
    environment:
      DATABASE_URL: "${DATABASE_URL:?set the PostgreSQL connection URL}"
      TRUST_PROXY: "${TRUST_PROXY:-false}"
      DURABULL_REDIS_URL_MAIN_PREFIX: "${DURABULL_REDIS_URL_MAIN_PREFIX:-bull}"

Add the corresponding values to .env.self-hosted, then include both files:

docker compose --env-file .env.self-hosted \
  -f tooling/docker/docker-compose.self-hosted.yaml \
  -f compose.override.yaml up -d

Changing DATABASE_URL selects a different database; it does not migrate existing PGlite data to PostgreSQL. Plan any data migration separately.

Preserve data

The shipped stack mounts durabull_data at /app/data and redis_data at /data.

  • PGlite data lives at /app/data/pglite by default.
  • If using docker run, mount a named volume at /app/data, or set DURABULL_PGLITE_DIR to a writable mounted directory.
  • Host bind mounts must be writable by UID/GID 1000:1000 (bun).
  • docker compose down preserves named volumes. docker compose down -v deletes them.
  • Back up the Durabull database, Redis data as needed, and encryption keys. A different encryption key cannot decrypt previously saved connection URLs or integration secrets.

For multiple API replicas, use a shared PostgreSQL database rather than sharing a PGlite directory.

Verify the deployment

curl -fsS http://localhost:3000/api/health
curl -fsS http://localhost:3000/api/app/config
curl -fsS http://localhost:3000/.well-known/oauth-protected-resource

Use your public origin for remote checks. Confirm the MCP resource equals {APP_BASE_URL}/mcp, then sign in and verify the selected Redis connection in the dashboard. Health alone does not validate Redis connectivity.

MCP uses the same app port; no additional port is needed. For OAuth smoke tests and incident triage, see the MCP operations runbook. Run its automated mcp:e2e checks only against disposable local or staging databases.

Upgrade and roll back

  1. Back up the database, encryption keys, and relevant Redis data.

  2. Set DURABULL_IMAGE in .env.self-hosted to the target published image tag.

  3. Pull and recreate the app with the same Compose files used for deployment. For the PostgreSQL override above:

    docker compose --env-file .env.self-hosted \
      -f tooling/docker/docker-compose.self-hosted.yaml \
      -f compose.override.yaml pull durabull
    docker compose --env-file .env.self-hosted \
      -f tooling/docker/docker-compose.self-hosted.yaml \
      -f compose.override.yaml up -d durabull

    If you use only the base stack, omit -f compose.override.yaml from both commands. Include any other override files in their original order. Omitting an override can change the database or other settings; Compose does not remember files from previous invocations.

  4. Repeat the health, login, and queue checks.

Database migrations run during initialization. Rolling back the image does not undo migrations; confirm schema compatibility or restore a compatible backup before downgrading.

Build an image from source

From the repository root:

docker build -f tooling/docker/Dockerfile -t durabull:local .

Set DURABULL_IMAGE=durabull:local to use it with Compose. The Dockerfile prunes the monorepo and uses separate dependency, build, and runtime stages.

Usage telemetry

Production images collect anonymous/pseudonymous usage telemetry. Configuring your own PostHog project does not disable Durabull telemetry. Read the telemetry disclosure for the collected fields and configuration.