# AI Cluster — target node: SOLARIA (compute/GPU). # # Authored from the live VPS stack at /home/dockeruser/docker/ai-cluster/ (read-only # recon 2026-07-27). The VPS stack is still running and is NOT touched by this file; # see CUTOVER.md for the switch-over plan. # # Deliberate differences from the VPS original: # - images built from ./src/* instead of pre-built local `ai-cluster-*` images # - openclaw publishes on SOLARIA's Tailscale IP only (was 0.0.0.0:8000) # - the external `npm_default` network is gone (it does not exist on SOLARIA; # NPM on the VPS will proxy to openclaw over Tailscale instead) # - mosquitto binds SOLARIA's Tailscale IP (was the VPS Tailscale IP) # - telegram-bot service is not carried over (it does not run on the VPS either) # - secrets come from env_file, not from ${} interpolation # # Secrets: /opt/homelab/config/ai-cluster/.env (see env.example). Compose validates # env_file paths at parse time, which is why service-ops-worker mounts that same # directory at the same absolute path — otherwise `docker compose ps` inside that # container fails to parse this file. services: redis: image: redis:7-alpine restart: unless-stopped volumes: - redis_data:/data networks: - ai-cluster mosquitto: image: eclipse-mosquitto:2 container_name: mosquitto restart: unless-stopped ports: # SOLARIA Tailscale IP. Never 0.0.0.0 — the broker has auth but is not # meant to be reachable off-mesh. - "100.100.231.104:1883:1883" volumes: # mosquitto.conf + acl ship in src/mosquitto/ and are copied to this path # at deploy; `passwd` is generated on the node and never committed. - /opt/homelab/config/ai-cluster/mosquitto:/mosquitto/config:ro networks: - ai-cluster openclaw: build: context: ./src/openclaw command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] restart: unless-stopped env_file: - /opt/homelab/config/ai-cluster/.env environment: REDIS_URL: redis://redis:6379/0 MQTT_HOST: mosquitto MQTT_PORT: 1883 ports: # Tailscale-only. NPM on the VPS proxies to 100.100.231.104:8000 over the # mesh — see CUTOVER.md. `healthcheck.sh` therefore probes the Tailscale # IP, not localhost. - "100.100.231.104:8000:8000" healthcheck: # The image is python:3.12-slim: no wget, no curl (verified against the # running VPS image). urllib is the only thing available. test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"] interval: 10s timeout: 5s retries: 5 depends_on: - redis - mosquitto networks: - ai-cluster codex-worker: # Builds from ./src/worker (default Dockerfile → worker.py). The VPS also has # a stale `codex-worker/worker.py` prototype that nothing builds — not carried # over; see service.yaml notes. build: context: ./src/worker restart: unless-stopped env_file: - /opt/homelab/config/ai-cluster/.env environment: # AGENT_ID/ROLE are MQTT routing targets. Kept as `vps-*` on purpose so # existing task producers keep working across the move; renaming is a # separate, coordinated change (CUTOVER.md, follow-up). AGENT_ID: vps-dev-1 ROLE: dev REDIS_URL: redis://redis:6379/0 MQTT_HOST: mosquitto MQTT_PORT: 1883 REQUEST_TIMEOUT_SECONDS: 30 depends_on: - redis - mosquitto networks: - ai-cluster planner-worker: build: context: ./src/worker dockerfile: Dockerfile.planner restart: unless-stopped env_file: - /opt/homelab/config/ai-cluster/.env environment: AGENT_ID: vps-planner-1 ROLE: planner MQTT_HOST: mosquitto MQTT_PORT: 1883 depends_on: - mosquitto networks: - ai-cluster service-ops-worker: build: context: ./src/worker dockerfile: Dockerfile.service-ops restart: unless-stopped env_file: - /opt/homelab/config/ai-cluster/.env environment: AGENT_ID: vps-service-ops-1 ROLE: service-ops MQTT_HOST: mosquitto MQTT_PORT: 1883 COMPOSE_PROJECT_NAME: ai-cluster volumes: # This worker shells out to `docker compose ps|restart` from /app, so it # needs the compose file and a .env in its cwd for interpolation. # The repo-path mount is host-specific → hosts/solaria/runtime/ai-cluster/. - /var/run/docker.sock:/var/run/docker.sock - /opt/homelab/config/ai-cluster/.env:/app/.env:ro # Same absolute path as the env_file directive above, so compose can parse # the mounted compose file from inside this container. - /opt/homelab/config/ai-cluster:/opt/homelab/config/ai-cluster:ro depends_on: - mosquitto networks: - ai-cluster volumes: redis_data: networks: ai-cluster: driver: bridge