Docker Compose
NimbleBrain ships two container images: platform (agent engine + API on port 27247, internal) and web (React UI served by Caddy, exposed on port 27246). Docker Compose runs both on a single machine with a shared internal network.
Prerequisites
Section titled “Prerequisites”- Docker Engine 24+ and Docker Compose v2
- An Anthropic API key
- At least 2 GB of available memory
Compose file
Section titled “Compose file”Create a project directory and add these two files:
{ "$schema": "https://schemas.nimblebrain.ai/nimblebrain.json", "bundles": [], "skillDirs": ["/data/skills"]}services: platform: image: nimblebrain/platform:latest build: . restart: unless-stopped volumes: - workspace:/data - ./nimblebrain.json:/app/nimblebrain.json:ro - ./skills:/app/skills:ro environment: - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY:-} - GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY:-} - NB_API_KEY=${NB_API_KEY} networks: - internal
web: image: nimblebrain/web:latest build: web/ restart: unless-stopped ports: - "27246:8080" environment: - PLATFORM_URL=http://platform:27247 depends_on: platform: condition: service_healthy networks: - internal
volumes: workspace:
networks: internal:What each service does
Section titled “What each service does”| Service | Image | Port | Purpose |
|---|---|---|---|
platform | nimblebrain/platform | 3000 (internal) | Agent engine, HTTP API, MCP bundle manager |
web | nimblebrain/web | 27246 (host) → 8080 (container) | React SPA served by Caddy, proxies /v1/* to platform |
The web container runs Caddy, which serves the static UI files and reverse-proxies all /v1/* API requests to the platform container. The platform container is not exposed to the host — all traffic flows through the web container.
Volumes
Section titled “Volumes”| Volume/Mount | Container path | Purpose |
|---|---|---|
workspace (named volume) | /data | Workspace data — conversations, logs, installed bundles, configs |
./nimblebrain.json (bind mount, read-only) | /app/nimblebrain.json | Platform configuration |
./skills (bind mount, read-only) | /app/skills | Custom skill files |
Environment variables
Section titled “Environment variables”| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY | Yes | Your Anthropic API key for Claude |
OPENAI_API_KEY | No | OpenAI API key (for OpenAI model provider) |
GOOGLE_GENERATIVE_AI_API_KEY | No | Google Gemini API key (for Google model provider) |
NB_API_KEY | Recommended | API authentication key. Minimum 8 characters. Protects all endpoints except /v1/health |
ALLOWED_ORIGINS | Production | Comma-separated list of allowed CORS origins (e.g., https://nb.example.com). Required for cross-origin cookie auth |
Health check
Section titled “Health check”The platform image has a built-in health check:
HEALTHCHECK --interval=30s --timeout=5s \ CMD curl -f http://localhost:27247/v1/health || exit 1The web service uses depends_on with condition: service_healthy, so it only starts after the platform passes its first health check.
Start the stack
Section titled “Start the stack”-
Set environment variables
Create a
.envfile in your project directory:.env ANTHROPIC_API_KEY=sk-ant-api03-your-key-hereNB_API_KEY=change-me-to-a-strong-secret-at-least-32-charsOr export them directly:
Terminal window export ANTHROPIC_API_KEY=sk-ant-api03-your-key-hereexport NB_API_KEY=change-me-to-a-strong-secret-at-least-32-chars -
Start the services
Terminal window docker compose up -d✔ Network project_internal Created✔ Volume "project_workspace" Created✔ Container project-platform-1 Healthy✔ Container project-web-1 Started -
Verify
Terminal window docker compose psNAME STATUS PORTSproject-platform-1 running (healthy) 27247/tcpproject-web-1 running 0.0.0.0:27246->8080/tcpCheck the health endpoint directly:
Terminal window curl http://localhost:27246/v1/health{"status":"ok","uptime":42} -
Open the UI
Go to http://localhost:27246. Enter your
NB_API_KEYvalue to log in.
Stop and restart
Section titled “Stop and restart”# Stop (preserves volumes)docker compose down
# Stop and remove volumes (deletes all workspace data)docker compose down -v
# Restartdocker compose restartUpdate to a new version
Section titled “Update to a new version”-
Pull the latest images
Terminal window docker compose pull -
Recreate the containers
Terminal window docker compose up -dCompose replaces only the containers whose images changed. The
workspacevolume persists across updates.
Backup and restore
Section titled “Backup and restore”Workspace data lives in the workspace named Docker volume. The default mount point is /data inside the platform container.
Backup
Section titled “Backup”docker run --rm \ -v project_workspace:/data \ -v "$(pwd)":/backup \ alpine tar czf /backup/nimblebrain-backup.tar.gz -C /data .Restore
Section titled “Restore”docker compose down
docker run --rm \ -v project_workspace:/data \ -v "$(pwd)":/backup \ alpine sh -c "rm -rf /data/* && tar xzf /backup/nimblebrain-backup.tar.gz -C /data"
docker compose up -dPlatform image details
Section titled “Platform image details”The nimblebrain/platform image is built from oven/bun:1.3-alpine and includes:
| Component | Version | Purpose |
|---|---|---|
| Bun | 1.3 | JavaScript/TypeScript runtime |
| Python | 3.13 | Runs Python-based MCP bundles |
| Node.js + npm | System | Runs Node-based MCP bundles |
| mpak CLI | Latest | Downloads and manages bundles from the mpak registry |
| curl | System | Used by the built-in health check |
The entrypoint is bun run src/cli/index.ts serve, which starts the HTTP API server on port 27247 with NB_WORK_DIR=/data and NB_HOST=0.0.0.0.
Reverse proxy
Section titled “Reverse proxy”The web container’s Caddy server already handles reverse proxying for the default setup. If you want to place your own reverse proxy in front of the stack (for TLS termination, custom domain, etc.), point it at port 27246:
server { listen 443 ssl; server_name nb.example.com;
ssl_certificate /etc/ssl/certs/nb.example.com.pem; ssl_certificate_key /etc/ssl/private/nb.example.com-key.pem;
location / { proxy_pass http://127.0.0.1:27246; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_buffering off; }}Troubleshooting
Section titled “Troubleshooting”Platform container keeps restarting
Section titled “Platform container keeps restarting”Check the logs:
docker compose logs platformCommon causes:
ANTHROPIC_API_KEYis missing or invalidNB_API_KEYis set but fewer than 8 characters — the server exits withNB_API_KEY is too short (minimum 8 characters)
Web container won’t start
Section titled “Web container won’t start”The web container waits for the platform health check to pass. If the platform is unhealthy, the web container stays in a “waiting” state. Fix the platform first.
Port 27246 already in use
Section titled “Port 27246 already in use”Change the host port mapping in docker-compose.yml:
ports: - "9090:8080" # Access the UI on port 9090 instead