Docker¶
Images¶
| Image | Build | For |
|---|---|---|
CPU (api target) |
docker build --target api -t brainbrew-api . |
OpenAI or any OpenAI-compatible endpoint; the CLI |
| GPU (default) | docker build -t brainbrew . |
LoRA training (NVIDIA driver R580+, CUDA 13.0) |
vLLM is not installed in either image. For a local model server, compose.yaml
runs the official vLLM image next to the app (below).
Each release also publishes the CPU image, signed with Sigstore (keyless) and with
build provenance, for linux/amd64 and linux/arm64:
docker pull ghcr.io/yog-sotho/brainbrew # or :<version>
docker run -p 127.0.0.1:8501:8501 --env-file .env -v brainbrew-runs:/app/runs ghcr.io/yog-sotho/brainbrew
The GPU image is not published (its CUDA torch stack is too large for the hosted build runners); build it with the command above.
Images run as a non-root user (uid 10001), log JSON lines, and never contain
.env or .streamlit/secrets.toml (.dockerignore is an allow-list).
docker run -p 127.0.0.1:8501:8501 --env-file .env -v brainbrew-runs:/app/runs brainbrew-api
Mount a volume at /app/runs to keep runs across restarts.
Brainbrew with a local vLLM server¶
compose.yaml runs the CPU app next to the official vLLM server image:
cp .env.sample .env # HF_TOKEN only for gated models or publishing
docker compose up -d # model: VLLM_MODEL (default Qwen/Qwen3-4B-Instruct-2507)
The app can only reach the vLLM container (BRAINBREW_ALLOW_CUSTOM_ENDPOINTS=0),
and vLLM's port is not published on the host. The first start downloads the
model, so the health check allows ten minutes.
Headless runs¶
docker run --rm -v "$PWD:/data" -v brainbrew-runs:/app/runs -e OPENAI_API_KEY brainbrew-api \
python cli.py run /data/notes.pdf -o /data/dataset.jsonl