Ctrl K
ComunidadGuías…

docker-compose.self-host.yml

Rama develop · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
name: roomote

# One shared application image ships web, api, controller, bullmq,
# preview-proxy, and the db-migrate one-shot. db-migrate owns the build (it
# runs first); every other service runs the same image with a different
# entrypoint-dispatcher command (see .docker/app/entrypoint.sh).
x-roomote-app-image: &roomote-app-image roomote-app:self-host

x-roomote-env: &roomote-env
  NODE_ENV: development
  R_APP_ENV: development
  ROOMOTE_DOCKER_LOAD_ENV_FILE: 'false'
  HOST: 0.0.0.0
  DATABASE_URL: postgres://postgres:password@postgres:5432/roomote_development
  REDIS_URL: redis://redis:6379
  S3_ENDPOINT: ${S3_ENDPOINT:-http://minio:9000}
  S3_PRESIGN_ENDPOINT: ${S3_PRESIGN_ENDPOINT:-http://minio:9000}
  S3_REGION: ${S3_REGION:-us-east-1}
  S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-roomote}
  S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-roomote-local-artifacts-password}
  S3_BUCKET_ARTIFACTS: ${S3_BUCKET_ARTIFACTS:-roomote-artifacts}
  R_APP_URL: ${R_PUBLIC_URL:-http://localhost:13000}
  R_INSTANCE_ID: ${R_INSTANCE_ID:-}
  R_CURATED_INTEGRATIONS_DISABLED: ${R_CURATED_INTEGRATIONS_DISABLED:-false}
  # The Brain: optional provider keys for the shared memory agents consult.
  # They power the brain service's embeddings and synthesis; an enabled Brain
  # otherwise falls back to the deployment's regular provider key. Admins turn
  # Memory on or off in Settings > Memory. Until that toggle has been set, a
  # key here also turns Memory on (legacy behavior).
  R_BRAIN_OPENROUTER_API_KEY: ${R_BRAIN_OPENROUTER_API_KEY:-}
  R_BRAIN_OPENAI_API_KEY: ${R_BRAIN_OPENAI_API_KEY:-}
  # Shared secret the Brain presents to /api/brain/inference. REQUIRED to
  # enable the Brain: a provider key alone leaves it unable to embed. The
  # installer generates one; set any 32+ random characters by hand.
  R_BRAIN_GATEWAY_TOKEN: ${R_BRAIN_GATEWAY_TOKEN:-}
  # Which models the Brain runs, in the configured provider's own naming.
  # The synthesis model is free to change; the embedding pair is create-time.
  R_BRAIN_MODEL: ${R_BRAIN_MODEL:-}
  R_BRAIN_EMBEDDING_MODEL: ${R_BRAIN_EMBEDDING_MODEL:-}
  R_BRAIN_EMBEDDING_DIMENSIONS: ${R_BRAIN_EMBEDDING_DIMENSIONS:-}
  R_GBRAIN_URL: ${R_GBRAIN_URL:-http://gbrain:8931}
  # Roomote reads the brain's bootstrap token once to register its own
  # scoped clients; api and bullmq mount the brain volume read-only.
  R_GBRAIN_ADMIN_TOKEN_FILE: /gbrain-data/admin-bootstrap-token
  # Written by the Brain on first boot when no token was supplied, so a stack
  # started by hand needs only a provider key.
  R_BRAIN_GATEWAY_TOKEN_FILE: /gbrain-data/gateway-token
  TRPC_URL: http://api:3001
  PREVIEW_PROXY_BASE_URL: ${PREVIEW_PROXY_BASE_URL:-http://localhost:18081}
  PREVIEW_DOMAINS: ${PREVIEW_DOMAINS:-localhost,127.0.0.1,roomotepreview.localhost}
  R_PUBLIC_URL: ${R_PUBLIC_URL:-}
  R_ALLOWED_EMAILS: ${R_ALLOWED_EMAILS:-}
  R_LICENSE_KEY: ${R_LICENSE_KEY:-}
  R_MODEL: ${R_MODEL:-}
  # Decision model. Unset, a deployment uses Jev when a TypeSafe key is
  # configured and otherwise none. R_JUDGMENT_UPSTREAM_URL points at any
  # server that speaks the decision contract, such as the `judgment` profile's
  # sidecar below (http://judgment:8080).
  R_JUDGMENT_MODEL: ${R_JUDGMENT_MODEL:-}
  R_JUDGMENT_UPSTREAM_URL: ${R_JUDGMENT_UPSTREAM_URL:-}
  R_JUDGMENT_UPSTREAM_API_KEY: ${R_JUDGMENT_UPSTREAM_API_KEY:-}
  R_SMALL_MODEL: ${R_SMALL_MODEL:-}
  R_CODE_REVIEW_MODEL: ${R_CODE_REVIEW_MODEL:-}
  R_EXPLORE_MODEL: ${R_EXPLORE_MODEL:-}
  R_MODEL_ENV_KEYS: ${R_MODEL_ENV_KEYS:-}
  CUSTOM_PROVIDER_API_KEY: ${CUSTOM_PROVIDER_API_KEY:-}
  OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
  AI_GATEWAY_API_KEY: ${AI_GATEWAY_API_KEY:-}
  OPENAI_API_KEY: ${OPENAI_API_KEY:-}
  ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
  DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}
  GEMINI_API_KEY: ${GEMINI_API_KEY:-}
  GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY:-}
  AWS_BEARER_TOKEN_BEDROCK: ${AWS_BEARER_TOKEN_BEDROCK:-}
  AWS_REGION: ${AWS_REGION:-}
  MOONSHOT_API_KEY: ${MOONSHOT_API_KEY:-}
  KIMI_API_KEY: ${KIMI_API_KEY:-}
  MINIMAX_API_KEY: ${MINIMAX_API_KEY:-}
  ZAI_API_KEY: ${ZAI_API_KEY:-}
  ZAI_REGION: ${ZAI_REGION:-}
  ZAI_CODING_PLAN_API_KEY: ${ZAI_CODING_PLAN_API_KEY:-}
  ZAI_CODING_PLAN_REGION: ${ZAI_CODING_PLAN_REGION:-}
  OPENCODE_API_KEY: ${OPENCODE_API_KEY:-}
  OPENCODE_GO_API_KEY: ${OPENCODE_GO_API_KEY:-}
  BASETEN_API_KEY: ${BASETEN_API_KEY:-}
  TOGETHER_API_KEY: ${TOGETHER_API_KEY:-}
  GITHUB_TOKEN: ${GITHUB_TOKEN:-}
  R_GITHUB_APP_ID: ${R_GITHUB_APP_ID:-}
  R_GITHUB_APP_PRIVATE_KEY: ${R_GITHUB_APP_PRIVATE_KEY:-}
  R_GITHUB_CLIENT_ID: ${R_GITHUB_CLIENT_ID:-}
  R_GITHUB_CLIENT_SECRET: ${R_GITHUB_CLIENT_SECRET:-}
  R_GITHUB_WEBHOOK_SECRET: ${R_GITHUB_WEBHOOK_SECRET:-}
  SLACK_APP_ID: ${SLACK_APP_ID:-}
  R_SLACK_CLIENT_ID: ${R_SLACK_CLIENT_ID:-}
  R_SLACK_CLIENT_SECRET: ${R_SLACK_CLIENT_SECRET:-}
  R_SLACK_SIGNING_SECRET: ${R_SLACK_SIGNING_SECRET:-}
  R_TELEGRAM_BOT_TOKEN: ${R_TELEGRAM_BOT_TOKEN:-}
  R_TELEGRAM_WEBHOOK_SECRET: ${R_TELEGRAM_WEBHOOK_SECRET:-}
  R_DISCORD_BOT_TOKEN: ${R_DISCORD_BOT_TOKEN:-}
  R_DISCORD_GATEWAY_SECRET: ${R_DISCORD_GATEWAY_SECRET:-}
  R_LINEAR_CLIENT_ID: ${R_LINEAR_CLIENT_ID:-}
  R_LINEAR_CLIENT_SECRET: ${R_LINEAR_CLIENT_SECRET:-}
  R_LINEAR_WEBHOOK_SECRET: ${R_LINEAR_WEBHOOK_SECRET:-}
  R_GITHUB_APP_SLUG: ${R_GITHUB_APP_SLUG:-}
  R_GITHUB_ADDITIONAL_APP_SLUGS: ${R_GITHUB_ADDITIONAL_APP_SLUGS:-}
  SETUP_TOKEN: ${SETUP_TOKEN:-}
  DEFAULT_COMPUTE_PROVIDER: ${DEFAULT_COMPUTE_PROVIDER:-docker}
  BL_API_KEY: ${BL_API_KEY:-}
  BL_WORKSPACE: ${BL_WORKSPACE:-}
  BLAXEL_IMAGE: ${BLAXEL_IMAGE:-}
  BLAXEL_REGION: ${BLAXEL_REGION:-}
  BLAXEL_STANDBY_MAX_COUNT: ${BLAXEL_STANDBY_MAX_COUNT:-25}
  BLAXEL_STANDBY_MAX_AGE_HOURS: ${BLAXEL_STANDBY_MAX_AGE_HOURS:-168}
  BOX_API_KEY: ${BOX_API_KEY:-}
  BOX_API_BASE_URL: ${BOX_API_BASE_URL:-}
  BOX_MACHINE_TYPE: ${BOX_MACHINE_TYPE:-}
  BOX_TIMEOUT_MS: ${BOX_TIMEOUT_MS:-}
  BOX_STANDBY_MAX_COUNT: ${BOX_STANDBY_MAX_COUNT:-25}
  BOX_STANDBY_MAX_AGE_HOURS: ${BOX_STANDBY_MAX_AGE_HOURS:-168}
  AZURE_SUBSCRIPTION_ID: ${AZURE_SUBSCRIPTION_ID:-}
  AZURE_RESOURCE_GROUP: ${AZURE_RESOURCE_GROUP:-}
  AZURE_SANDBOX_GROUP: ${AZURE_SANDBOX_GROUP:-}
  AZURE_SANDBOX_REGION: ${AZURE_SANDBOX_REGION:-}
  AZURE_SANDBOX_DISK_IMAGE: ${AZURE_SANDBOX_DISK_IMAGE:-}
  AZURE_CLIENT_ID: ${AZURE_CLIENT_ID:-}
  AZURE_TENANT_ID: ${AZURE_TENANT_ID:-}
  AZURE_CLIENT_SECRET: ${AZURE_CLIENT_SECRET:-}
  AZURE_SANDBOX_REGISTRY_USERNAME: ${AZURE_SANDBOX_REGISTRY_USERNAME:-}
  AZURE_SANDBOX_REGISTRY_TOKEN: ${AZURE_SANDBOX_REGISTRY_TOKEN:-}
  AZURE_SANDBOX_SIZE: ${AZURE_SANDBOX_SIZE:-}
  AZURE_SANDBOX_EGRESS_INSPECTION: ${AZURE_SANDBOX_EGRESS_INSPECTION:-}
  AZURE_HTTP_DEBUG: ${AZURE_HTTP_DEBUG:-}
  DOCKER_STANDBY_MAX_COUNT: ${DOCKER_STANDBY_MAX_COUNT:-10}
  DOCKER_STANDBY_MAX_AGE_HOURS: ${DOCKER_STANDBY_MAX_AGE_HOURS:-24}
  # Declarative environment provisioning: point ROOMOTE_ENVIRONMENTS_DIR at a
  # directory of environment YAML files mounted into the api container (for
  # example `- ./environments:/roomote/environments:ro` on the api service),
  # or put inline multi-document YAML in ROOMOTE_ENVIRONMENTS_YAML. Only the
  # api service reads these; see SELF_HOSTING.md.
  ROOMOTE_ENVIRONMENTS_DIR: ${ROOMOTE_ENVIRONMENTS_DIR:-}
  ROOMOTE_ENVIRONMENTS_YAML: ${ROOMOTE_ENVIRONMENTS_YAML:-}

x-roomote-service: &roomote-service
  restart: unless-stopped
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_started
    minio-init:
      condition: service_completed_successfully
    db-migrate:
      condition: service_completed_successfully
  environment:
    <<: *roomote-env

services:
  db-migrate:
    restart: 'no'
    container_name: roomote-db-migrate
    image: *roomote-app-image
    build:
      context: .
      dockerfile: .docker/app/Dockerfile
      args:
        APP_ENV: development
        RELEASE_VERSION: ${RELEASE_VERSION:-self-host-local}
    environment:
      <<: *roomote-env
    depends_on:
      postgres:
        condition: service_healthy
    command: ['db-migrate']

  web:
    <<: *roomote-service
    container_name: roomote-web
    image: *roomote-app-image
    command: ['web']
    # Publish app ports to loopback only. Standalone self-host is meant to sit
    # behind a reverse proxy on the same host (or the production Caddy overlay,
    # which resets these to none); binding to all interfaces would expose the
    # web app, API, queue dashboard, and preview proxy directly.
    ports:
      - '127.0.0.1:13000:3000'
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
      minio-init:
        condition: service_completed_successfully
      db-migrate:
        condition: service_completed_successfully
      api:
        condition: service_started
    environment:
      <<: *roomote-env
      NODE_ENV: production

  api:
    <<: *roomote-service
    container_name: roomote-api
    image: *roomote-app-image
    command: ['api']
    # Reachable from task worker containers (worker -> API over TRPC_URL).
    networks: [default, worker]
    environment:
      <<: *roomote-env
      PORT: 3001
    volumes:
      - gbrain_data:/gbrain-data:ro
    ports:
      - '127.0.0.1:13001:3001'

  controller:
    <<: *roomote-service
    container_name: roomote-controller
    image: *roomote-app-image
    command: ['controller']
    environment:
      <<: *roomote-env
      SANDBOX_OPENROUTER_API_KEY: ${SANDBOX_OPENROUTER_API_KEY:-}
    # Spawns and reaches task worker containers on the worker network.
    networks: [default, worker]
    stop_grace_period: 7m
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
      minio-init:
        condition: service_completed_successfully
      db-migrate:
        condition: service_completed_successfully
      api:
        condition: service_started
    healthcheck:
      test:
        [
          'CMD-SHELL',
          'curl -fsS --max-time 5 http://api:3001/health/controller >/dev/null',
        ]
      interval: 5s
      timeout: 6s
      retries: 12
      start_period: 20s

  bullmq:
    <<: *roomote-service
    container_name: roomote-bullmq
    image: *roomote-app-image
    command: ['bullmq']
    environment:
      <<: *roomote-env
      PORT: 3002
    volumes:
      - gbrain_data:/gbrain-data:ro
    ports:
      - '127.0.0.1:13002:3002'
    healthcheck:
      test:
        [
          'CMD-SHELL',
          'curl -fsS --max-time 5 http://127.0.0.1:3002/admin/health >/dev/null',
        ]
      interval: 5s
      timeout: 6s
      retries: 12
      start_period: 20s

  preview-proxy:
    <<: *roomote-service
    container_name: roomote-preview-proxy
    image: *roomote-app-image
    command: ['preview-proxy']
    # Proxies preview traffic into task worker containers.
    networks: [default, worker]
    environment:
      <<: *roomote-env
      PORT: 8081
    ports:
      - '127.0.0.1:18081:8081'

  # Brain (deployment-hosted gbrain). Opt-in via
  # COMPOSE_PROFILES=brain; deployments that never enable the
  # profile see no infrastructure change. Internal-only: no published
  # ports, reachable solely by the API service, which fronts it at
  # /api/mcp/gbrain with the read-only agent credential. Wire static tokens
  # into api/bullmq (operator-pinned), or the normal path: api/bullmq already
  # point R_GBRAIN_URL at http://gbrain:8931 and read the admin bootstrap token
  # from the shared volume, so Roomote registers its own scoped clients via
  # the admin API once Memory is enabled in Settings > Memory. Set
  # GBRAIN_ADMIN_BOOTSTRAP_TOKEN below (>= 32 chars of [A-Za-z0-9_-]) to pin
  # the token; if left unset, the entrypoint generates one at
  # /data/admin-bootstrap-token on the volume.
  gbrain:
    profiles: [brain]
    container_name: roomote-gbrain
    build: .docker/gbrain
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    networks: [default]
    environment:
      GBRAIN_DATABASE_URL: postgres://postgres:password@postgres:5432/roomote_development
      GBRAIN_ADMIN_BOOTSTRAP_TOKEN: ${GBRAIN_ADMIN_BOOTSTRAP_TOKEN:-}
      # The Brain holds no provider key. It calls back into this deployment,
      # which injects whichever provider key an admin configured in Settings,
      # so rotating or switching that key needs no restart here. Setting a
      # provider key below instead makes the Brain call the provider directly.
      OPENAI_BASE_URL: ${GBRAIN_OPENAI_BASE_URL:-http://api:3001/api/brain/inference}
      OPENAI_API_KEY: ${R_BRAIN_GATEWAY_TOKEN:-}
      # Create-time, and it has to be the container that receives these: the
      # embedding model and its width are decided when the Brain is created,
      # and the gateway must never substitute a different one afterwards.
      # Set both before the first boot, or neither. The synthesis model is
      # the opposite (R_BRAIN_MODEL, applied by the gateway per request).
      GBRAIN_EMBEDDING_MODEL: ${R_BRAIN_EMBEDDING_MODEL:-}
      GBRAIN_EMBEDDING_DIMENSIONS: ${R_BRAIN_EMBEDDING_DIMENSIONS:-}
    volumes:
      - gbrain_data:/data

  # Optional CPU decision model (judgment sidecar): the Memory check, reply
  # addressing, and Roomote's other typed decisions without a Jev key or a
  # GPU. Enable with COMPOSE_PROFILES=judgment and point Roomote at it with
  # R_JUDGMENT_UPSTREAM_URL=http://judgment:8080. It serves
  # roomote/roomote-judgment-gliner unless JUDGMENT_MODEL names another
  # GLiNER 2.5 checkpoint. The memory and CPU caps bound what inference can
  # take from the host.
  judgment:
    profiles: [judgment]
    container_name: roomote-judgment
    build: .docker/judgment
    restart: unless-stopped
    networks: [default]
    environment:
      JUDGMENT_MODEL: ${JUDGMENT_MODEL:-roomote/roomote-judgment-gliner}
      HF_TOKEN: ${JUDGMENT_HF_TOKEN:-}
      JUDGMENT_API_KEY: ${R_JUDGMENT_UPSTREAM_API_KEY:-}
      JUDGMENT_THREADS: ${JUDGMENT_CPUS:-4}
    mem_limit: ${JUDGMENT_MEMORY_LIMIT:-4g}
    cpus: ${JUDGMENT_CPUS:-4}
    volumes:
      - judgment_models:/models

networks:
  # The controller uses this network to discover the API and optional preview
  # proxy. Each task gets a separate bridge network; only the API and an
  # enabled preview proxy are attached to it, so task sandboxes never share a
  # network with one another or with Postgres, Redis, or MinIO. The default
  # network is implicit (roomote_default) and carries the datastores.
  worker:
    name: roomote_worker
    driver: bridge

volumes:
  gbrain_data:
    name: roomote_gbrain_data
  judgment_models:
    name: roomote_judgment_models