# ============================================================================= # rust-brain — Docker Compose # ============================================================================= # Production-grade Rust code intelligence platform # ============================================================================= services: # --------------------------------------------------------------------------- # DATABASE LAYER # --------------------------------------------------------------------------- postgres: build: context: . dockerfile: Dockerfile.postgres args: AGE_COMMIT: ${AGE_COMMIT:-8c74fd24dd91bba54fdf4793fccd0fb7bd2bb9d8} image: rustbrain-postgres:latest container_name: rustbrain-postgres ports: - "${POSTGRES_PORT:-5432}:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 6G pgweb: image: sosedoff/pgweb:latest container_name: rustbrain-pgweb ports: - "${PGWEB_PORT:-8085}:8081" environment: PGWEB_DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable" healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:8081/ || exit 1"] interval: 15s timeout: 5s retries: 5 depends_on: postgres: condition: service_healthy restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 256M neo4j: image: neo4j:5-community container_name: rustbrain-neo4j ports: - "${NEO4J_HTTP_PORT:-7474}:7474" - "${NEO4J_BOLT_PORT:-7687}:7687" volumes: - neo4j_data:/data - neo4j_logs:/logs - ./configs/neo4j/neo4j.conf:/conf/neo4j.conf - ./scripts/init-neo4j.cypher:/var/lib/neo4j/import/init.cypher environment: # NEO4J_AUTH is derived from NEO4J_USER and NEO4J_PASSWORD to prevent password drift. # Never set NEO4J_AUTH separately — always keep NEO4J_USER and NEO4J_PASSWORD in sync. NEO4J_AUTH: ${NEO4J_USER:-neo4j}/${NEO4J_PASSWORD} NEO4J_PLUGINS: '["apoc"]' healthcheck: test: ["CMD-SHELL", "cypher-shell -u neo4j -p ${NEO4J_PASSWORD} 'RETURN 1' || exit 1"] interval: 15s timeout: 10s retries: 10 start_period: 30s restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 12G qdrant: image: qdrant/qdrant:v1.12.1 container_name: rustbrain-qdrant ports: - "${QDRANT_REST_PORT:-6333}:6333" - "${QDRANT_GRPC_PORT:-6334}:6334" volumes: - qdrant_data:/qdrant/storage environment: QDRANT__SERVICE__GRPC_PORT: "6334" QDRANT__SERVICE__ENABLE_TLS: "false" healthcheck: test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/6333'"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: rustbrain-net: aliases: - qdrant deploy: resources: limits: memory: 12G # --------------------------------------------------------------------------- # AI / ML LAYER # --------------------------------------------------------------------------- ollama: image: ollama/ollama:latest container_name: rustbrain-ollama ports: - "${OLLAMA_PORT:-11434}:11434" environment: # Enable parallel request processing for better GPU utilization OLLAMA_NUM_PARALLEL: "4" # Keep models loaded for faster subsequent requests OLLAMA_KEEP_ALIVE: "30m" volumes: - ollama_data:/root/.ollama healthcheck: test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/11434'"] interval: 15s timeout: 10s retries: 10 start_period: 60s restart: unless-stopped networks: rustbrain-net: aliases: - ollama deploy: resources: limits: memory: 16G reservations: devices: - driver: nvidia count: all capabilities: [gpu] # --------------------------------------------------------------------------- # OBSERVABILITY LAYER # --------------------------------------------------------------------------- postgres-exporter: image: prometheuscommunity/postgres-exporter:v0.15.0 container_name: rustbrain-postgres-exporter environment: DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable" depends_on: postgres: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:9187/metrics || exit 1"] interval: 15s timeout: 5s retries: 3 restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 128M blackbox-exporter: image: prom/blackbox-exporter:v0.25.0 container_name: rustbrain-blackbox-exporter ports: - "${BLACKBOX_EXPORTER_PORT:-9115}:9115" volumes: - ./configs/prometheus/blackbox.yml:/etc/blackbox_exporter/config.yml healthcheck: test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:9115 || exit 1"] interval: 15s timeout: 5s retries: 3 restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 64M node-exporter: image: prom/node-exporter:v1.8.0 container_name: rustbrain-node-exporter ports: - "${NODE_EXPORTER_PORT:-9100}:9100" healthcheck: test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:9100/metrics || exit 1"] interval: 15s timeout: 5s retries: 3 restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 128M prometheus: image: prom/prometheus:v2.51.0 container_name: rustbrain-prometheus ports: - "${PROMETHEUS_PORT:-9090}:9090" volumes: - ./configs/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - ./configs/prometheus/alert_rules.yml:/etc/prometheus/alert_rules.yml - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.retention.time=30d' - '--web.enable-lifecycle' healthcheck: test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:9090/-/healthy || exit 1"] interval: 15s timeout: 5s retries: 5 depends_on: - postgres-exporter - node-exporter restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 512M grafana: image: grafana/grafana:11.0.0 container_name: rustbrain-grafana ports: - "${GRAFANA_PORT:-3000}:3000" volumes: - grafana_data:/var/lib/grafana - ./configs/grafana/provisioning:/etc/grafana/provisioning - ./configs/grafana/dashboards:/var/lib/grafana/dashboards environment: GF_SECURITY_ADMIN_USER: ${GRAFANA_USER:-admin} GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-rustbrain} GF_INSTALL_PLUGINS: grafana-clock-panel depends_on: - prometheus healthcheck: test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:3000/api/health || exit 1"] interval: 15s timeout: 5s retries: 5 restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 512M # --------------------------------------------------------------------------- # API LAYER # --------------------------------------------------------------------------- api: build: context: . dockerfile: services/api/Dockerfile container_name: rustbrain-api ports: - "${API_PORT:-8088}:8080" environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} NEO4J_URL: bolt://neo4j:7687 NEO4J_USER: neo4j NEO4J_PASSWORD: ${NEO4J_PASSWORD} QDRANT_HOST: http://qdrant:6333 QDRANT_COLLECTION: code_embeddings OLLAMA_HOST: http://ollama:11434 EMBEDDING_MODEL: ${EMBEDDING_MODEL:-qwen3-embedding:4b} EMBEDDING_DIMENSIONS: ${EMBEDDING_DIMENSIONS:-2560} RUST_LOG: ${RUST_LOG:-info,rustbrain_api=debug} API_PORT: 8080 OPENCODE_HOST: http://opencode:4096 # Docker socket access for per-workspace volume orchestration. # Override with DOCKER_HOST env var if using a remote daemon. DOCKER_HOST: ${DOCKER_HOST:-unix:///var/run/docker.sock} # GH_TOKEN: set in .env for private repos (uncomment next line). # Empty GH_TOKEN breaks public repo cloning (uses gh CLI instead of git). # GH_TOKEN: ${GH_TOKEN:-} # Host-side clone root — must match the host path that backs the bind # mount below so docker run -v :... works from the API container. WORKSPACE_HOST_CLONE_ROOT: ${WORKSPACE_HOST_CLONE_ROOT:-/tmp/rustbrain-clones} # Ingestion image triggered per-workspace after clone completes. INGESTION_IMAGE: ${INGESTION_IMAGE:-rustbrain-ingestion:latest} OPENCODE_IMAGE: ${OPENCODE_IMAGE:-rustbrain-opencode:latest} DOCKER_NETWORK: ${DOCKER_NETWORK:-rustbrain_rustbrain-net} RUSTBRAIN_PUBLIC_HOST: ${RUSTBRAIN_PUBLIC_HOST:-} RUSTBRAIN_CONTAINER_KEEP_ALIVE_SECS: ${CONTAINER_KEEP_ALIVE_SECS:-0} OPENCODE_CONFIG_HOST_PATH: ${OPENCODE_CONFIG_HOST_PATH:-} MCP_SSE_URL: ${MCP_SSE_URL:-http://mcp-sse:3001/sse} LITELLM_API_KEY: ${LITELLM_API_KEY:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-} OPENCODE_SERVER_PASSWORD: ${OPENCODE_SERVER_PASSWORD:-} RUSTBRAIN_AUTH_DISABLED: "true" networks: rustbrain-net: aliases: - api volumes: - ./.snapshot-manifest.json:/app/.snapshot-manifest.json:ro # Docker socket mount — required for DockerClient (workspace volume CRUD). # The API container calls `docker volume create/rm/inspect` at runtime. - /var/run/docker.sock:/var/run/docker.sock # Shared clone directory — bind-mounted so the host Docker daemon can # mount workspace clone paths into the ingestion container. - ${WORKSPACE_HOST_CLONE_ROOT:-/tmp/rustbrain-clones}:/tmp/rustbrain-clones # Grant the rustbrain user access to /var/run/docker.sock. # DOCKER_GID must match the host docker group gid (check: stat -c %g /var/run/docker.sock). # Common values: 999 (Ubuntu), 125 (Debian-based), 998 (Fedora). group_add: - "${DOCKER_GID:-999}" depends_on: postgres: condition: service_healthy neo4j: condition: service_healthy qdrant: condition: service_healthy ollama: condition: service_healthy healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 10s restart: unless-stopped deploy: resources: limits: memory: 2G # --------------------------------------------------------------------------- # MCP LAYER # --------------------------------------------------------------------------- mcp: build: context: ./services/mcp dockerfile: Dockerfile container_name: rustbrain-mcp environment: API_BASE_URL: http://api:8080 RUST_LOG: ${RUST_LOG:-info,rustbrain_mcp=debug} MCP_TRANSPORT: stdio WORKSPACE_ID: ${WORKSPACE_ID:-} depends_on: api: condition: service_healthy restart: "no" networks: [rustbrain-net] deploy: resources: limits: memory: 256M # MCP server uses stdio, so we don't expose ports # To use: docker exec -i rustbrain-mcp rustbrain-mcp # --------------------------------------------------------------------------- # AI GATEWAY LAYER # --------------------------------------------------------------------------- # LiteLLM is hosted externally at https://grid.ai.juspay.net # OpenCode connects to it via LITELLM_BASE_URL and LITELLM_API_KEY mcp-sse: build: context: ./services/mcp dockerfile: Dockerfile container_name: rustbrain-mcp-sse ports: - "${MCP_SSE_PORT:-3001}:3001" environment: API_BASE_URL: http://api:8080 RUST_LOG: ${RUST_LOG:-info,rustbrain_mcp=debug} MCP_TRANSPORT: sse MCP_PORT: 3001 WORKSPACE_ID: ${WORKSPACE_ID:-} healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:3001/health || exit 1"] interval: 15s timeout: 10s retries: 5 start_period: 20s depends_on: api: condition: service_healthy restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 256M opencode: build: context: ./configs/opencode dockerfile: Dockerfile container_name: rustbrain-opencode ports: - "${OPENCODE_PORT:-4096}:4096" environment: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} OPENAI_API_KEY: ${OPENAI_API_KEY} OPENCODE_SERVER_PASSWORD: ${OPENCODE_SERVER_PASSWORD} LITELLM_BASE_URL: ${LITELLM_BASE_URL} LITELLM_API_KEY: ${LITELLM_API_KEY} MCP_SSE_URL: ${MCP_SSE_URL:-http://mcp-sse:3001/sse} CHAT_MODEL: ${CHAT_MODEL:-glm-latest} # Git-based write workflow configuration TARGET_REPO_URL: ${TARGET_REPO_URL:-} TARGET_REPO_PATH: /workspace/target-repo OPENCODE_WORK_DIR: ${OPENCODE_WORK_DIR:-/workspace/target-repo-work} GIT_USER_NAME: ${GIT_USER_NAME:-OpenCode Developer} GIT_USER_EMAIL: ${GIT_USER_EMAIL:-opencode@rustbrain.local} FEATURE_BRANCH_PREFIX: ${FEATURE_BRANCH_PREFIX:-opencode} # GH_TOKEN: set in .env for private repo access (uncomment next line) # GH_TOKEN: ${GH_TOKEN:-} volumes: - opencode_data:/home/opencode/.local/share/opencode - ./configs/opencode/opencode.json:/home/opencode/.config/opencode/opencode.json.template:ro - ./configs/opencode/.opencode:/home/opencode/.config/opencode/.opencode:ro - ./:/home/opencode/projects/rust-brain:ro - ${TARGET_REPO_PATH:-.}:/workspace/target-repo:ro - opencode_work:/workspace/target-repo-work healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:4096/ || exit 1"] interval: 15s timeout: 10s retries: 5 start_period: 600s depends_on: mcp-sse: condition: service_healthy restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 512M # --------------------------------------------------------------------------- # EDITOR PLAYGROUND FRONTEND # --------------------------------------------------------------------------- playground-ui: build: context: ./frontend dockerfile: Dockerfile args: VITE_API_BASE_URL: ${PLAYGROUND_API_BASE_URL:-http://localhost:8088} container_name: rustbrain-playground-ui ports: - "${PLAYGROUND_UI_PORT:-8092}:80" healthcheck: test: ["CMD-SHELL", "wget -qO /dev/null http://127.0.0.1/healthz || exit 1"] interval: 10s timeout: 3s retries: 5 start_period: 10s depends_on: api: condition: service_healthy restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 64M # --------------------------------------------------------------------------- # INGESTION LAYER # --------------------------------------------------------------------------- # Audit service — workspace leak detection and compliance scanning # Runs periodic Cypher queries against Neo4j to detect cross-workspace # contamination and orphan nodes. Also detects orphaned Docker volumes # and containers not tracked in Postgres. Exposes metrics at :8090/metrics. audit: build: context: . dockerfile: services/audit/Dockerfile container_name: rustbrain-audit environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} NEO4J_URL: bolt://neo4j:7687 NEO4J_USER: neo4j NEO4J_PASSWORD: ${NEO4J_PASSWORD} AUDIT_PORT: "8090" AUDIT_INTERVAL_SECS: ${AUDIT_INTERVAL_SECS:-600} LEAK_DETECTION_DRY_RUN: ${LEAK_DETECTION_DRY_RUN:-true} AUDIT_LOG_RETENTION_DAYS: ${AUDIT_LOG_RETENTION_DAYS:-90} RUST_LOG: ${RUST_LOG:-info,rustbrain_audit=debug} ports: - "${AUDIT_PORT:-8090}:8090" volumes: # Docker socket access for detecting orphaned volumes/containers - /var/run/docker.sock:/var/run/docker.sock:ro # Grant access to Docker socket (same GID as API container) group_add: - "${DOCKER_GID:-999}" healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8090/health || exit 1"] interval: 30s timeout: 10s retries: 3 depends_on: postgres: condition: service_healthy neo4j: condition: service_healthy restart: unless-stopped networks: [rustbrain-net] deploy: resources: limits: memory: 256M ingestion: build: context: . dockerfile: services/ingestion/Dockerfile image: rustbrain-ingestion:latest container_name: rustbrain-ingestion volumes: - ${HOME}/.cargo/registry:/home/rustbrain/.cargo/registry:ro - ${HOME}/.cargo/git:/home/rustbrain/.cargo/git:ro - rustbrain-expand-cache:/tmp/rustbrain-expand-cache # NOTE: target-repo is mounted via `docker compose run -v` in ingest.sh environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} NEO4J_URL: bolt://neo4j:7687 NEO4J_USER: neo4j NEO4J_PASSWORD: ${NEO4J_PASSWORD} QDRANT_HOST: http://qdrant:6333 OLLAMA_HOST: http://ollama:11434 EMBEDDING_MODEL: ${EMBEDDING_MODEL:-qwen3-embedding:4b} EMBEDDING_DIMENSIONS: ${EMBEDDING_DIMENSIONS:-2560} WORKSPACE_PATH: /workspace/target-repo RUST_LOG: ${RUST_LOG:-info,rustbrain_ingestion=debug} HEALTH_PORT: "${HEALTH_PORT:-9091}" # Memory budget for ingestion pipeline INGESTION_MEMORY_BUDGET: "32GB" ports: - "${HEALTH_PORT:-9091}:9091" depends_on: postgres: condition: service_healthy neo4j: condition: service_healthy qdrant: condition: service_healthy ollama: condition: service_healthy networks: [rustbrain-net] deploy: resources: limits: memory: 32G cpus: "20" reservations: memory: 8G cpus: "10" # Auto-remove container after run to prevent accumulation # Use: docker compose run --rm ingestion profiles: - ingestion # --------------------------------------------------------------------------- # NETWORKS & VOLUMES # --------------------------------------------------------------------------- networks: rustbrain-net: driver: bridge ipam: config: - subnet: 172.28.0.0/16 volumes: postgres_data: neo4j_data: neo4j_logs: qdrant_data: ollama_data: prometheus_data: grafana_data: opencode_data: opencode_work: rustbrain-expand-cache: