Durabull Documentation

Docker Image and Compose

Run Durabull with the official Docker image, then scale to a production-friendly Compose deployment.

Docker is the fastest way to get a self-hosted Durabull instance running.

Production Docker images collect anonymous/pseudonymous usage telemetry to understand feature usage and improve Durabull. Configuring POSTHOG_KEY for your own PostHog project does not disable Durabull telemetry; your project still receives the full PostHog browser analytics stream. Durabull does not collect Redis URLs, queue names, Redis key names, job data, logs, emails, names, organizations, or raw error messages.

1) Choose an Image Tag

Use the published image from GHCR:

  • ghcr.io/durabullhq/durabull:<version>

For local evaluation, latest is fine. For production, pin an explicit version tag.

2) Quick Run with docker run

This starts Redis and Durabull on a shared Docker network:

docker network create durabull

docker run -d --name durabull-redis --network durabull redis:8-alpine

docker run --rm -p 127.0.0.1:3000:3000 --network durabull \
  -e DURABULL_AUTHLESS=true \
  -e MCP_AUTHLESS_BEARER_TOKEN="$(openssl rand -hex 32)" \
  -e DURABULL_ENV_CONNECTIONS=true \
  -e DURABULL_REDIS_URL_ENCRYPTION_KEY=$(openssl rand -hex 32) \
  -e DURABULL_REDIS_URL_MAIN=redis://durabull-redis:6379 \
  -e DURABULL_REDIS_URL_MAIN_ENVIRONMENT=development \
  -e DURABULL_REDIS_URL_DEFAULT=MAIN \
  -e APP_BASE_URL=http://localhost:3000 \
  -e VITE_PUBLIC_APP_URL=http://localhost:3000 \
  ghcr.io/durabullhq/durabull:latest

This quick-start uses authless mode for local evaluation only on localhost (127.0.0.1:3000). Authless grants owner-level access to the entire web UI and API, not only /mcp. MCP_AUTHLESS_BEARER_TOKEN protects MCP only. Never expose authless on LAN/WAN without perimeter controls.

Production images use NODE_ENV=production and require MCP_AUTHLESS_BEARER_TOKEN when authless is enabled. For internet-facing deploys, use Compose with DURABULL_AUTHLESS=false and BETTER_AUTH_SECRET.

Open http://localhost:3000, then confirm:

  • GET /api/health returns status: ok
  • GET /api/app/config returns JSON instead of 500
  • the queue dashboard loads for your default connection
  • MCP discovery: GET /.well-known/oauth-protected-resource returns resource = http://localhost:3000/mcp

MCP uses the same port as the app (3000). Do not publish a separate MCP port.

3) Docker Compose (Durabull + Redis)

Use the maintained Compose file at tooling/docker/docker-compose.self-hosted.yaml:

  • Single app port (3000 by default) serves API, web, and MCP at {APP_BASE_URL}/mcp
  • Defaults DURABULL_AUTHLESS to false (OAuth production path)
  • For local authless evaluation only, set DURABULL_AUTHLESS=true and a strong MCP_AUTHLESS_BEARER_TOKEN when NODE_ENV=production
  • Redis is not published to the host in the shipped Compose file (internal network only). Do not add 6379:6379 in production

Start it with:

export DURABULL_REDIS_URL_ENCRYPTION_KEY="$(openssl rand -hex 32)"
export BETTER_AUTH_SECRET="$(openssl rand -hex 32)"
# Optional for production: export APP_BASE_URL=https://your-domain.example
docker compose -f tooling/docker/docker-compose.self-hosted.yaml up -d

Then validate:

  • GET /api/health
  • GET /api/app/config
  • GET /api/auth/get-session (authless mode) or OAuth sign-in (default Compose)
  • GET /.well-known/oauth-protected-resource (MCP resource URI)
  • Full MCP smoke (staging/local only): see MCP operations runbook

4) Persistence Notes

When DATABASE_URL is unset, PGlite data is file-backed inside the container.

  • Default location: /app/data/pglite
  • For persistence, mount /app/data or set DURABULL_PGLITE_DIR to a mounted path.
  • Named Docker volumes are the simplest option for local deployments.
  • If you bind-mount a host directory, ensure it is writable by container UID/GID 1000:1000 (bun).

Older Image Workaround

If you are pinned to an older image that fails with EACCES: permission denied, mkdir '/app/data' or returns 500 for /api/app/config, use one of these temporary workarounds:

  • upgrade to an image that includes the writable /app/data fix
  • set DURABULL_PGLITE_DIR to a writable path inside the container for ephemeral evaluation
  • switch to PostgreSQL mode with DATABASE_URL

5) Production Notes

For internet-facing deployments:

  • set DURABULL_AUTHLESS=false
  • configure BETTER_AUTH_SECRET
  • configure DURABULL_REDIS_URL_ENCRYPTION_KEY
  • set correct APP_BASE_URL and VITE_PUBLIC_APP_URL (required for MCP OAuth resource {APP_BASE_URL}/mcp)
  • review Security and Hardening
  • use PostgreSQL (DATABASE_URL) for stateful team environments
  • after deploy, follow MCP operations runbook (use staging for mcp:e2e)

MCP on self-hosted Docker

  • Endpoint: {APP_BASE_URL}/mcp on port 3000 (or DURABULL_APP_PORT)
  • Production (internet-facing): DURABULL_AUTHLESS=false, OAuth, required APP_BASE_URL
  • Authless is for isolated lab networks only — not a substitute for OAuth on production ingress
  • Details: MCP Server

6) Build Image from Source

Source Dockerfile path:

  • tooling/docker/Dockerfile
  • tooling/docker/docker-compose.self-hosted.yaml

It uses turbo prune and multi-stage builds for optimized production images.