Docker quickstart¶
Bring up the SecondBrain Memory API in a single container with
docker compose up. This is the shortest path from a clean machine to a
running, tokened HTTP API that other agents and integrations can call.
Prerequisites¶
- Docker Engine 24+ with the
docker composeplugin (Docker Desktop, OrbStack, Colima, or native Linux Docker all work) - ~3 GB free disk for the image and the first cold-start model download
- A free TCP port
8765on the host
A bearer token is required for any /v1/* Memory API call. The compose file
can accept SB_SERVE_TOKEN from the shell. Generate one through the built-in
token manager instead of relying on a placeholder:
sb serve-token env creates a local serve_auth.json token if one does not
already exist, prints an export SB_SERVE_TOKEN=... line, and keeps the token
file at owner-only 0600 permissions.
Quickstart¶
From the repo root:
That target prints a banner, runs docker compose build, runs
docker compose up -d, polls /health until it returns 200, and prints the
exact curl you need for your first Memory API call. If SB_SERVE_TOKEN is
not set, the target generates a secure local token with sb serve-token.
If you prefer the raw commands:
eval "$(uv run sb serve-token env)"
docker compose build
docker compose up -d
# Wait ~30–60s on first run while the FastAPI app and embedding model warm up.
curl http://localhost:8765/health
/health does not require auth; it returns
{"status": "ok", "host": "127.0.0.1"} when the daemon is ready. The host
field reflects the in-app default and is harmless — the listener itself binds
to 0.0.0.0:8765 inside the container.
State is persisted across restarts via two host bind-mounts that compose creates next to the repo:
| Path on host | Path in container | What lives there |
|---|---|---|
./state |
/data/state |
runtime db, vector index, event log, model cache |
./vault |
/data/vault |
your markdown knowledge base |
Drop a .md file into ./vault/00_inbox/ on the host and it is immediately
visible to ingest commands inside the container.
First Memory API call¶
Every /v1/* route requires Authorization: Bearer <SB_SERVE_TOKEN>.
A canonical first call is the memory recall route:
TOKEN="${SB_SERVE_TOKEN:-$(uv run sb serve-token show --create)}"
curl -H "Authorization: Bearer ${TOKEN}" \
"http://localhost:8765/v1/memory/recall?query=hello&top_k=3"
For an audited inspection of recent runtime events:
The very first recall after a fresh build can take 10–60 seconds while
sentence-transformers downloads its default embedding model into
./state/hf_cache. Subsequent calls hit the local cache and return in
milliseconds. This warm-up is expected, not a bug.
The full route surface, request/response shapes, and error contract live in
contracts/memory_api_v1.yaml (in the repo root). Treat that
file as the source of truth — anything documented there is callable against
the running container with the same bearer token used above.
Observing the daemon¶
docker compose ps # running container + health status
docker compose logs -f secondbrain # live logs
docker compose exec secondbrain sb doctor # in-container diagnostics
Stopping and resetting¶
docker compose down # stop the container, keep ./state and ./vault
docker compose down -v # also drop named volumes (bind mounts survive)
rm -rf ./state ./vault # full local reset (destructive)
Troubleshooting¶
/healthnever responds. First boot pulls and compiles sentence-transformers wheels; expect up to 90 s. If it still hangs after that, inspectdocker compose logs secondbrain.401 Unauthorizedon/v1/*. TheAuthorization: Bearer …header is missing or does not matchSB_SERVE_TOKEN. The token is read from the environment at container start; restart compose after exporting a new value. Usesb serve-token statusto inspect the local token state without printing the secret, oreval "$(uv run sb serve-token env)"to export the active token into your current shell.- Port 8765 already bound. Either stop the conflicting process or remap
in
docker-compose.yml:"18765:8765"then calllocalhost:18765. - Permission errors writing to
./stateor./vault. The container runs as UID 1000. On Linux hosts where your shell user is not UID 1000, runsudo chown -R 1000:1000 ./state ./vaultonce.