Running AGI Orchestrator in Docker
The provided image bundles the orchestrator server + dashboard + the claude CLI. It
deliberately does not bundle two things, because doing so would break the project’s core
guarantee:
| Not in the image | Why | How it’s provided |
|---|---|---|
| Your Claude subscription login | Baking credentials into an image is unsafe, and the tool must draw from your subscription | Mount the host’s ~/.claude |
| The local brain model (Qwen) | The brain must talk to a loopback endpoint (subscription-safety check), and models are large | Run LM Studio / Ollama on the host and use host networking |
Read SECURITY.md first. A container that can start sessions has, in effect, shell access to every mounted project directory. Don’t expose the port publicly without a token and a TLS tunnel.
The host-networking constraint (important)
The brain’s provider baseUrl must resolve to localhost / 127.0.0.1 / ::1 — the
orchestrator refuses a non-loopback URL to stay subscription-safe (src/config.ts). Inside a
normal bridge-networked container, localhost is the container, not your host, so it can’t
reach the model.
The clean fix is host networking (network_mode: host in the compose file): the container
shares the host’s network stack, so http://localhost:11434/v1 reaches the host’s Ollama and
passes the loopback check.
- Linux hosts: host networking works natively. This is the supported, recommended setup.
- Docker Desktop (macOS / Windows): host networking is
more limited. Two options:
- Enable “Host networking” in Docker Desktop settings (recent versions support it), or
- Use bridge networking +
host.docker.internal, and run the container with an env flag that the loopback check treats as local. The current code does not provide such an escape hatch —host.docker.internalwould be rejected. So on Docker Desktop, prefer option 1, or run the orchestrator natively (npm start) and containerize only your other services.
If you need host.docker.internal support, that’s a reasonable feature request — see
CONTRIBUTING.md; it would relax the loopback check to also accept the
Docker gateway host only when an explicit opt-in env var is set.
Quick start (Linux host, recommended)
# 1. One-time host setup
claude # then /login — authenticates your subscription
ollama serve & # or start LM Studio; must expose an OpenAI-compatible API
ollama pull qwen3:8b # a fast instruct model for the brain
# 2. Configure
cp config.example.json config.json
# - set provider.baseUrl to http://localhost:11434/v1 (Ollama) or :1234/v1 (LM Studio)
# - set provider.model to what your server reports under /v1/models
# - set each session's cwd to a path you will bind-mount (see below)
# 3. Build & run
docker compose up --build
Open http://localhost:4317.
Mounting your project directories
Each session’s cwd in config.json is a path inside the container. For claude to actually
edit your code, that path must be bind-mounted. The simplest convention is to mount your projects
at the same absolute path the host uses, so config paths “just work”:
# docker-compose.yml → services.agi.volumes:
- /home/you/projects:/home/you/projects
Then a session with "cwd": "/home/you/projects/my-app" resolves correctly in both worlds.
Using the plain Dockerfile (no compose)
docker build -t agi-orchestrator:latest .
docker run --rm -it \
--network host \
-e AGI_PORT=4317 \
-v "$PWD/config.json:/app/config.json:ro" \
-v "$HOME/.claude:/home/node/.claude" \
-v agi-data:/data \
-v "/home/you/projects:/home/you/projects" \
agi-orchestrator:latest
Optional: bundle Ollama as a second container
If you’d rather not run Ollama on the host, add an Ollama service. Because the brain check
requires loopback, the orchestrator and Ollama must share a network namespace — the easiest
way is to keep the orchestrator on network_mode: host and also run Ollama with host
networking, so localhost:11434 reaches it. Create docker-compose.ollama.yml:
services:
ollama:
image: ollama/ollama:latest
network_mode: host # exposes 11434 on the host loopback
volumes:
- ollama-models:/root/.ollama
restart: unless-stopped
# For GPU acceleration (NVIDIA), add:
# deploy:
# resources:
# reservations:
# devices:
# - capabilities: [gpu]
volumes:
ollama-models:
Run both, then pull a model into the container:
docker compose -f docker-compose.yml -f docker-compose.ollama.yml up --build -d
docker compose exec ollama ollama pull qwen3:8b
Keep provider.baseUrl as http://localhost:11434/v1. Note: CPU-only inference is slow;
the brain runs once per turn, so pass a GPU through for a usable pace (see the deploy block
above, and Docker’s GPU docs).
Configuration reference
| Env var | Purpose | Default |
|---|---|---|
AGI_PORT |
Dashboard port | 4317 |
AGI_CONFIG |
Path to config JSON inside the container | /app/config.json |
AGI_DB |
SQLite store path (mount it as a volume) | /data/agi.db |
AGI_DISPATCH_TOKEN |
Remote-access token (see SECURITY.md) | unset (remote refused) |
All other settings live in config.json — see the
configuration reference and
schemas/config.schema.json.
Troubleshooting
config.provider.baseUrl must be a local endpoint— you’re not on host networking, or you pointed the brain at a remote/bridge host. Usenetwork_mode: hostand alocalhostURL.Refusing to start: ANTHROPIC_API_KEY …— an API-billing env var is set. Unset it; the container must use the mounted subscription login, not an API key. (This guard is intentional.)- claude says it’s not logged in — you didn’t mount
~/.claude, or you never ran/loginon the host. Authenticate on the host first, then mount it. - The agent can’t find/edit files — the session’s
cwdisn’t bind-mounted, or the in- container path differs fromconfig.json. Mount projects at matching absolute paths. - Brain calls are very slow — CPU inference. Use a smaller/faster model or pass a GPU through to the model server.
- Live screen / gates don’t render in headless engine — expected:
claude-headlesshas no TUI. Use the defaultclaudeengine for the interactive dashboard experience.