############################################################################## # GoClaw Control Center — Docker Stack (Docker Swarm Production) # # LLM Provider: # By default the gateway uses Ollama Cloud (https://ollama.com/v1). # Set LLM_BASE_URL and LLM_API_KEY via Docker secrets or environment. # # To use a local Ollama instance on a GPU-equipped Swarm node: # 1. Uncomment the "ollama" service below. # 2. Add the label gpu=true to the GPU node: # docker node update --label-add gpu=true # 3. Change LLM_BASE_URL in the gateway service to: http://ollama:11434 # # Deploy: # # Create required secrets first: # echo "rootpass" | docker secret create mysql-root-password - # echo "pass" | docker secret create mysql-password - # echo "jwtsecret"| docker secret create jwt-secret - # echo "ollamakey"| docker secret create llm-api-key - # # docker stack deploy -c docker/docker-stack.yml goclaw # # Remove: # docker stack rm goclaw # # Scale gateway: # docker service scale goclaw_gateway=3 ############################################################################## version: "3.9" networks: goclaw-net: driver: overlay attachable: true volumes: mysql-data: driver: local # ollama-data: # Uncomment when using local Ollama service below # driver: local secrets: mysql-root-password: external: true mysql-password: external: true jwt-secret: external: true llm-api-key: external: true services: # ── MySQL 8 ────────────────────────────────────────────────────────────── db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql-root-password MYSQL_DATABASE: goclaw MYSQL_USER: goclaw MYSQL_PASSWORD_FILE: /run/secrets/mysql-password secrets: - mysql-root-password - mysql-password volumes: - mysql-data:/var/lib/mysql networks: - goclaw-net deploy: replicas: 1 placement: constraints: - node.role == manager restart_policy: condition: on-failure delay: 10s resources: limits: memory: 1G reservations: memory: 512M healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 start_period: 30s # ── Local Ollama LLM Server (GPU ONLY — disabled by default) ───────────── # Uncomment this entire block only on Swarm nodes with a compatible GPU. # After uncommenting, also change gateway LLM_BASE_URL to http://ollama:11434 # and remove the llm-api-key secret from gateway (not needed for local Ollama). # # ollama: # image: ollama/ollama:latest # volumes: # - ollama-data:/root/.ollama # networks: # - goclaw-net # deploy: # replicas: 1 # placement: # constraints: # # Pin to GPU-labelled node: docker node update --label-add gpu=true # - node.labels.gpu == true # restart_policy: # condition: on-failure # delay: 15s # resources: # limits: # memory: 16G # reservations: # memory: 4G # # NVIDIA GPU support — uncomment on GPU-enabled nodes: # # runtime: nvidia # # environment: # # - NVIDIA_VISIBLE_DEVICES=all # ── Go Gateway (Orchestrator + Tool Executor) ───────────────────────────── gateway: image: git.softuniq.eu/uniqai/goclaw/gateway:latest environment: PORT: "18789" # ── LLM Provider ───────────────────────────────────────────────────── # Default: Ollama Cloud (requires llm-api-key secret below) LLM_BASE_URL: "${LLM_BASE_URL:-https://ollama.com/v1}" DEFAULT_MODEL: "${DEFAULT_MODEL:-qwen2.5:7b}" # LLM_API_KEY is injected via /run/secrets/llm-api-key (see below) # ── To switch to local GPU Ollama, set: ────────────────────────────── # LLM_BASE_URL: "http://ollama:11434" # (and uncomment the ollama service above) # ───────────────────────────────────────────────────────────────────── DATABASE_URL: "goclaw:{{MYSQL_PASSWORD}}@tcp(db:3306)/goclaw?parseTime=true" PROJECT_ROOT: "/app" GATEWAY_REQUEST_TIMEOUT_SECS: "120" GATEWAY_MAX_TOOL_ITERATIONS: "10" LOG_LEVEL: "info" secrets: - mysql-password - source: llm-api-key target: /run/secrets/llm-api-key networks: - goclaw-net ports: - target: 18789 published: 18789 protocol: tcp mode: ingress volumes: # Docker socket for docker_exec tool - /var/run/docker.sock:/var/run/docker.sock deploy: replicas: 2 update_config: parallelism: 1 delay: 10s order: start-first failure_action: rollback rollback_config: parallelism: 1 delay: 5s restart_policy: condition: on-failure delay: 5s max_attempts: 3 resources: limits: memory: 512M cpus: "1.0" reservations: memory: 128M cpus: "0.25" healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:18789/health"] interval: 15s timeout: 5s retries: 3 start_period: 10s # ── Control Center (React + Node.js) ───────────────────────────────────── control-center: image: git.softuniq.eu/uniqai/goclaw/control-center:latest environment: NODE_ENV: production DATABASE_URL: "mysql://goclaw:{{MYSQL_PASSWORD}}@db:3306/goclaw" GATEWAY_URL: "http://gateway:18789" secrets: - mysql-password - jwt-secret networks: - goclaw-net ports: - target: 3000 published: 3000 protocol: tcp mode: ingress deploy: replicas: 2 update_config: parallelism: 1 delay: 10s order: start-first failure_action: rollback rollback_config: parallelism: 1 delay: 5s restart_policy: condition: on-failure delay: 5s max_attempts: 3 resources: limits: memory: 1G cpus: "1.0" reservations: memory: 256M cpus: "0.25" healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"] interval: 15s timeout: 5s retries: 3 start_period: 20s # ── Traefik Reverse Proxy (optional) ───────────────────────────────────── # traefik: # image: traefik:v3.0 # command: # - "--providers.docker.swarmMode=true" # - "--providers.docker.exposedbydefault=false" # - "--entrypoints.web.address=:80" # - "--entrypoints.websecure.address=:443" # - "--certificatesresolvers.letsencrypt.acme.email=admin@softuniq.eu" # - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" # - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" # ports: # - "80:80" # - "443:443" # volumes: # - /var/run/docker.sock:/var/run/docker.sock:ro # - traefik-certs:/letsencrypt # networks: # - goclaw-net # deploy: # placement: # constraints: # - node.role == manager