Docker Compose¶
Docker Compose is the team profile default and the fastest way to get a
complete MoE Sovereign stack running on a single host. The same
docker-compose.yaml that the development team uses is production-ready.
Firewall is mandatory in production
The compose stack publishes ports 8002 (orchestrator), 8003 (MCP),
8088 (admin UI), 8098 (ChromaDB) and 3001 (Grafana) on 0.0.0.0 —
they are reachable from any host that can route to this machine.
Several of these endpoints have no authentication (e.g.
POST /graph/knowledge/import) and assume network-level isolation.
Configure your host firewall to expose only 80/443 to the public
internet. See Firewall & Network Exposure for
ready-to-run UFW / firewalld / iptables rules.
What the stack contains¶
flowchart TB
subgraph CORE[Core services]
O[langgraph-app<br/>:8002 → :8000]
M[mcp-precision<br/>:8003]
A[moe-admin<br/>:8088]
end
subgraph DATA[Data tier]
PG[(terra_checkpoints<br/>postgres 17)]
VK[(terra_cache<br/>valkey)]
CH[(chromadb-vector<br/>:8001)]
NJ[(neo4j-knowledge<br/>:7474/:7687)]
KF[(moe-kafka<br/>KRaft :9092)]
end
subgraph OBS[Observability]
PR[(moe-prometheus<br/>:9090)]
GR[(moe-grafana<br/>:3001)]
NE[(node-exporter)]
CA[(cadvisor)]
end
subgraph EDGE[Edge]
CD[moe-caddy<br/>:80/:443]
DZ[moe-dozzle<br/>:9999]
end
CD --> O & A
O --> PG & VK & CH & NJ & KF & M
A --> PG & VK
PR -- scrape --> O & NE & CA
GR --> PR
classDef core fill:#eef2ff,stroke:#6366f1;
classDef data fill:#fef3c7,stroke:#d97706;
classDef obs fill:#ecfdf5,stroke:#059669;
classDef edge fill:#fce7f3,stroke:#db2777;
class O,M,A core;
class PG,VK,CH,NJ,KF data;
class PR,GR,NE,CA obs;
class CD,DZ edge;
Launch¶
# team profile (all 19 services)
sudo docker compose up -d
# solo profile — smaller resource footprint, optional services off
sudo docker compose -f docker-compose.yaml -f docker-compose.solo.yaml up -d
# enterprise profile — omit data-tier services, connect to external clusters
sudo docker compose -f docker-compose.yaml -f docker-compose.enterprise.yaml up -d
The profile overrides are additive: Compose merges the base file with the profile-specific file, so you never maintain two parallel copies.
Rebuilding after code changes¶
sudo docker compose build langgraph-app && sudo docker compose up -d langgraph-app
sudo docker compose build moe-admin && sudo docker compose up -d moe-admin
sudo docker compose build mcp-precision && sudo docker compose up -d mcp-precision
The Dockerfiles are multi-stage, so rebuilds only redo the layers that
actually changed — a typical main.py edit rebuilds in under 10 seconds.
Configuration via .env (no compose-file edits needed)¶
The shipped docker-compose.yml is fully parameterised. Every host path
and every published port resolves through ${VAR:-default} so you can
override them from .env without touching the compose file. This keeps
git pull conflict-free.
Required passwords¶
Compose enforces these with ${VAR:?…} — docker compose up aborts
with an explicit error if any are missing. install.sh /
bootstrap-macos.sh write all of them automatically.
.env key |
Used by |
|---|---|
POSTGRES_CHECKPOINT_PASSWORD |
terra_checkpoints, langgraph-app, moe-admin |
MOE_USERDB_PASSWORD |
langgraph-app, moe-admin |
REDIS_PASSWORD |
terra_cache, langgraph-app, moe-admin |
NEO4J_PASS |
neo4j-knowledge, mcp-precision, langgraph-app |
GF_SECURITY_ADMIN_PASSWORD |
moe-grafana |
ADMIN_PASSWORD, ADMIN_SECRET_KEY |
moe-admin, moe-dozzle-init |
Host data roots (bind-mount sources)¶
.env key |
Linux default | macOS default | Holds |
|---|---|---|---|
MOE_DATA_ROOT |
/opt/moe-infra |
$HOME/moe-data |
Postgres, Neo4j, Redis, ChromaDB, Kafka, Prometheus, logs |
GRAFANA_DATA_ROOT |
/opt/grafana |
$HOME/moe-grafana |
Grafana SQLite + dashboards |
FEW_SHOT_HOST_DIR |
${MOE_DATA_ROOT}/few-shot |
same | Few-shot example store |
CLAUDE_SKILLS_DIR |
./skills |
./skills |
Claude Code skill bundle |
Published host ports (override to avoid collisions)¶
All ports are remappable. Container-internal ports stay the same; only
the host side changes when you set <SERVICE>_HOST_PORT in .env.
| Default | .env key |
Service | Bind |
|---|---|---|---|
| 80 | CADDY_HTTP_PORT |
Caddy reverse proxy | 0.0.0.0 |
| 443 | CADDY_HTTPS_PORT |
Caddy HTTPS / HTTP3 | 0.0.0.0 |
| 8002 | LANGGRAPH_HOST_PORT |
Orchestrator API | 0.0.0.0 |
| 8003 | MCP_HOST_PORT |
MCP precision tools | 0.0.0.0 |
| 8088 | ADMIN_UI_HOST_PORT |
Admin UI | 0.0.0.0 |
| 8098 | DOCS_HOST_PORT |
Docs site | 0.0.0.0 |
| 3001 | GRAFANA_HOST_PORT |
Grafana | 0.0.0.0 |
| 8001 | CHROMA_HOST_PORT |
ChromaDB | 127.0.0.1 |
| 9090 | PROMETHEUS_HOST_PORT |
Prometheus | 127.0.0.1 |
| 7474 | NEO4J_HTTP_PORT |
Neo4j Browser | 127.0.0.1 |
| 7687 | NEO4J_BOLT_PORT |
Neo4j Bolt | 127.0.0.1 |
| 9092 | KAFKA_HOST_PORT |
Kafka broker | 127.0.0.1 |
| 9999 | DOZZLE_HOST_PORT |
Dozzle log viewer | 127.0.0.1 |
| 9100 | NODE_EXPORTER_HOST_PORT |
node-exporter | 127.0.0.1 |
| 9338 | CADVISOR_HOST_PORT |
cAdvisor | 127.0.0.1 |
Example .env snippet to move the Admin UI off the default 8088 because
your host already runs something there:
After editing .env, docker compose up -d re-creates only the
affected container.
See .env.example for the full annotated reference and
Firewall & Network Exposure for which services to
firewall on each binding interface.