Brainby arc-labs/docs
Self-host

Docker setup

Run the single Brain image — ports, the persistent data volume, the mandatory LLM key, and the io_uring runtime flags.

Prerequisites
  • Docker on a Linux host (or a Linux VM). Brain uses io_uring, which is Linux-only.
  • A valid LLM provider API key. The server hard-fails to boot without one.
  • Enough RAM for the bundled models. The full capability stack (embedder + reranker + classifier) loads several GiB into RAM; give the Docker VM headroom.
What you'll build

A single container serving the wire data plane on :8080 and health/metrics HTTP on :9091, backed by a persistent named volume. Curl-able and ready for a client to connect.

The image

The image is built from the repository's Dockerfile (a multi-stage build that compiles brain-server and copies it onto a slim Debian runtime). Build it once:

docker build -t brain:latest .

The build bakes a default config file to /etc/brain/config.toml and sets the entrypoint to brain-server --config /etc/brain/config.toml.

docker run

docker run -d --name brain \
  --security-opt seccomp=unconfined \
  --ulimit memlock=-1 \
  -p 8080:8080 \
  -p 9091:9091 \
  -v brain-data:/var/lib/brain/data \
  -v brain-models:/var/lib/brain/models \
  -e BRAIN__LLM__API_KEY="sk-..." \
  brain:latest

What each piece does:

required

--security-opt seccomp=unconfined

io_uring (Glommio's runtime) needs io_uring_setup/enter/register, which Docker's default seccomp profile blocks. Without an unconfined (or io_uring-allowing) profile the shard executor fails to start. Use a custom seccomp profile that allows the io_uring syscalls if unconfined is too broad for your environment.

required

--ulimit memlock=-1

io_uring and the mmap arena require a raised memlock rlimit.

required

BRAIN__LLM__API_KEY

The mandatory LLM provider key. The server refuses to boot without it (write-time HyPE + extraction depend on it). The provider is inferred from the model id — override the model with BRAIN__LLM__MODEL (e.g. gpt-4o-mini).

ports

-p 8080 / -p 9091

8080 is the data plane (the binary wire protocol clients and SDKs speak). 9091 is the public HTTP server — /healthz and /metrics.

storage

-v brain-data

The persistent data directory (/var/lib/brain/data) — arena, WAL, redb metadata, indexes. This is the one volume you must back up.

models

-v brain-models

Caches the bundled models (/var/lib/brain/models) after first download, so restarts don't re-fetch them.

The admin HTTP surface (/v1/*) binds to 127.0.0.1:9092 inside the container by config and is intentionally not published. Reach it from the host with docker exec brain ..., or front it with a token/mTLS proxy — never expose it raw. See Security.

Bring it up

# Watch it boot (models load, shards spawn)
docker logs -f brain

# Liveness — the metrics/health port
curl http://localhost:9091/healthz

If the LLM key is missing or invalid, the server exits during startup with a provider-validation error rather than running in a degraded mode — this is deliberate.

Compose

The same container via Compose:

name: brain

services:
  brain:
    image: brain:latest
    container_name: brain
    restart: unless-stopped
    ports:
      - "8080:8080"
      - "9091:9091"
    environment:
      BRAIN__LLM__API_KEY: ${BRAIN__LLM__API_KEY}
      # BRAIN__LLM__MODEL: gpt-4o-mini
    volumes:
      - brain-data:/var/lib/brain/data
      - brain-models:/var/lib/brain/models
    security_opt:
      - seccomp=unconfined
    ulimits:
      memlock: -1
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9091/healthz"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s

volumes:
  brain-data:
  brain-models:
docker compose up -d
docker compose logs -f brain

Overriding config

The image bakes a default config at /etc/brain/config.toml. Two ways to change it without rebuilding:

  • Per-field env override — set BRAIN__SECTION__FIELD (e.g. -e BRAIN__STORAGE__SHARD_COUNT=8). This is the only supported env form. See Configuration.
  • Whole-file override — bind-mount your own file: -v /host/brain.toml:/etc/brain/config.toml:ro.

Memory sizing

The bundled model stack loads into RAM at boot: the embedder, the cross-encoder reranker (when enabled), and the classifier. On a full stack this is several GiB before any data. If the Docker VM is too small the shard spawn OOMs while loading a model — give it comfortable headroom.

Volumes you back up

The single source of truth is the brain-data volume (/var/lib/brain/data) — arena, WAL, redb, and indexes all live there. The brain-models volume is a cache; it re-populates on first run if lost. Backup guidance in Backups.

Tearing it down

docker rm -f brain              # stop + remove container, keep volumes
docker volume rm brain-data     # destroys all stored memories — irreversible

Removing the brain-data volume deletes everything Brain has stored. There is no soft-delete at the Docker layer. Keep the volume unless you intend a clean wipe.

Was this page helpful?

On this page