Ctrl K
ComunidadGuías…

docker-compose.prod.yaml

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

Archivo original ↗Preguntar a KiBot
name: ${AXON_COMPOSE_PROJECT_NAME:-axon}

# Full axon stack: axon (HTTP), tei, chrome, and a mandatory bundled qdrant.
# All host-published application/provider ports are loopback-only. Production
# images are tag-plus-digest pinned; review and update both when upgrading.
# service — qdrant is NOT optional in this file; every `up -d` starts all 4.
#   docker compose --env-file ~/.axon/.env -f docker-compose.prod.yaml up -d
# For this deployment's specific override (qdrant lives on tootie for RAM
# reasons, not devhost), apply docker-compose.external-qdrant.yaml on top:
#   docker compose --env-file ~/.axon/.env \
#     -f docker-compose.prod.yaml -f docker-compose.external-qdrant.yaml up -d
# That override skips starting the bundled axon-qdrant container and sets
# QDRANT_URL directly from AXON_EXTERNAL_QDRANT_URL instead (overriding this
# file's own AXON_QDRANT_URL-based default) — see that file's comments.
# ~/.axon is the canonical host appdata root. Override AXON_HOME only when
# relocating the full Axon appdata tree to another absolute path.

x-common-service: &common-service
  networks:
    - axon
  restart: unless-stopped
  extra_hosts:
    - "host.docker.internal:host-gateway"
  ulimits:
    nofile:
      soft: 65535
      hard: 65535
  logging:
    driver: "json-file"
    options:
      max-size: "10m"
      max-file: "3"
      compress: "true"

x-gpu-service: &gpu-service
  deploy:
    resources:
      reservations:
        devices:
          - driver: nvidia
            count: 1
            capabilities: [gpu]

services:
  axon:
    <<: *common-service
    image: ${AXON_IMAGE:-ghcr.io/dinglebear-ai/axon:latest@sha256:611e79987bd056b99646da5c4297540840e5dca774fbbbe1fefa244129d95476}
    container_name: ${AXON_CONTAINER_NAME:-axon}
    user: "1000:1000"
    env_file:
      - path: ${AXON_HOME:-${HOME}/.axon}/.env
        required: false
    environment:
      # Host-bleed guards: container user is `axon` — override host paths.
      # The shared ~/.axon/.env has these set to the host's $HOME, which
      # doesn't exist inside the container. env_file values override Dockerfile
      # ENV, so we must re-override here.
      AXON_HOME: /home/axon/.axon
      AXON_DATA_DIR: /home/axon/.axon
      # Pin service URLs so the axon container does not inherit host-loopback
      # values from ~/.axon/.env. Bundled qdrant is the default (mandatory,
      # always started below); docker-compose.external-qdrant.yaml overrides
      # this to point at an external instance (e.g. tootie) instead.
      QDRANT_URL: ${AXON_QDRANT_URL:-http://axon-qdrant:6333}
      TEI_URL: http://axon-tei:80
      AXON_CHROME_REMOTE_URL: http://axon-chrome:6000
      # AXON_SERVER_URL is a client-side switch. The shared ~/.axon/.env may
      # set it for host CLI commands, but the server container must not inherit
      # it or container-local maintenance commands will route back through HTTP.
      AXON_SERVER_URL: ""
      # Host-only bootstrap overrides must not leak from the shared env file
      # into the container and change config/env loading paths.
      AXON_ENV_FILE: ""
      AXON_CONFIG_PATH: ""
      AXON_CODEX_CMD: ""
      # Leave AXON_CODEX_HOME empty so axon's source-home resolver falls through
      # to CODEX_HOME below (graceful when ~/.axon/codex isn't seeded yet).
      AXON_CODEX_HOME: ""
      # Persist the container's codex home INSIDE the already-mounted ~/.axon, so
      # config.toml (MCP servers), auth.json, refreshed OAuth tokens, and sessions
      # survive container recreates. Fresh/empty by default → fast init, and fully
      # separate from the operator's host ~/.codex. Seed it once on the host:
      #   CODEX_HOME=~/.axon/codex codex login
      #   (or: mkdir -p ~/.axon/codex && cp ~/.codex/auth.json ~/.axon/codex/)
      # API-key auth (OPENAI_API_KEY in ~/.axon/.env) works without seeding.
      CODEX_HOME: /home/axon/.axon/codex
      TZ: ${TZ:-America/New_York}
      # Filesystem-namespace claim affinity (axon_rust-p2oc). Embed jobs whose
      # input is a local path are stamped with the enqueueing process's namespace
      # at insert time, then only claimed by workers in the same namespace. The
      # container uses "axon-container"; host CLI workers leave this unset (or
      # set it explicitly) so they only claim namespace-NULL (URL/text) jobs and
      # their own local-path jobs — never container-path jobs.
      AXON_FS_NAMESPACE: axon-container
    volumes:
      # Persist config + secrets + jobs.db + output + logs + artifacts on the host.
      - ${AXON_HOME:-${HOME}/.axon}:/home/axon/.axon
      # Persist OAuth client registrations, refresh tokens, and JWT signing key
      # across container recreates so upstream gateways don't hold stale client IDs.
      - ${AXON_HOME:-${HOME}/.axon}/lab-auth:/home/axon/.lab
      # Gemini CLI auth files — headless ask/evaluate/research synthesis reads
      # oauth_creds.json, gemini-credentials.json, google_accounts.json from here.
      # Prerequisite: run `gemini auth login` on the host before starting the container
      # so ${HOME}/.gemini exists and contains valid credentials. If the directory is
      # absent, Docker will create it as root-owned — run `mkdir -p ~/.gemini` first.
      - ${GEMINI_HOME:-${HOME}/.gemini}:/home/axon/.gemini:ro
      # NOTE: codex auth/config is NOT a separate mount — the container's codex
      # home is CODEX_HOME=/home/axon/.axon/codex (see environment above), which
      # already persists via the ~/.axon bind mount on the first volume line.
    ports:
      - "127.0.0.1:${AXON_HTTP_PUBLISH:-8001}:8001"
    command: ["serve"]
    depends_on:
      axon-tei:
        condition: service_healthy
      axon-chrome:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS --max-time 4 http://127.0.0.1:8001/healthz >/dev/null || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 180s

  axon-qdrant:
    <<: *common-service
    image: qdrant/qdrant:v1.18.2@sha256:75eab8c4ba42096724fdcfde8b4de0b5713d529dde32f285a1f86fdcb2c9e50c
    container_name: ${AXON_QDRANT_CONTAINER_NAME:-axon-qdrant}
    # Shield the vector DB from the kernel's *global* OOM killer: under host
    # memory pressure (concurrent cargo builds + optimizer churn) qdrant is the
    # largest RSS on the box and got picked twice in 10 minutes (dmesg
    # CONSTRAINT_NONE — host-level, not this container's cgroup cap). Prefer
    # killing a rebuildable build process over the database.
    oom_score_adj: -500
    ports:
      - "127.0.0.1:${QDRANT_HTTP_PORT:-53333}:6333"
      - "127.0.0.1:${QDRANT_GRPC_PORT:-53334}:6334"  # gRPC
    volumes:
      - ${AXON_HOME:-${HOME}/.axon}/qdrant:/qdrant/storage
      - ./config/qdrant/production.yaml:/qdrant/config/production.yaml:ro
    deploy:
      resources:
        limits:
          # axon collection's resident working set (int8 quant always_ram + in-RAM
          # HNSW index) stably sits ~12.5G and was OOM-killed against a 12G cap
          # (206 restarts, dmesg CONSTRAINT_MEMCG). 16G gives ~28% headroom over
          # the stable set. To reduce real RAM instead, flip the axon collection's
          # quantization always_ram:false + hnsw on_disk:true (trades query latency).
          memory: 16G
        reservations:
          memory: 1G
    healthcheck:
      test:
        [
          "CMD",
          "/usr/bin/bash",
          "-ec",
          "exec 3<>/dev/tcp/127.0.0.1/6333; printf \"GET /readyz HTTP/1.1\\r\\nHost: 127.0.0.1\\r\\nConnection: close\\r\\n\\r\\n\" >&3; head -n 1 <&3 | grep -q \"200\"",
        ]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 120s

  axon-tei:
    <<: [*common-service, *gpu-service]
    image: ghcr.io/huggingface/text-embeddings-inference:89-1.9@sha256:e47e625ced2385d3dbfdee79ba0380204578e0b27ef1a926783f9b3486aaf109
    container_name: ${AXON_TEI_CONTAINER_NAME:-axon-tei}
    environment:
      # Container-internal path — not suitable for shared .env.
      HF_HUB_CACHE: /data/cache
      # TEI-specific filter — would suppress axon's logs if in shared .env.
      RUST_LOG: text_embeddings_router=info
    ports:
      - "127.0.0.1:${TEI_HTTP_PORT:-52000}:80"
    volumes:
      - ${AXON_HOME:-${HOME}/.axon}/tei:/data
    # TEI server-side tuning below is compose-only interpolation — these vars
    # have no config.toml home because they configure the TEI *container*
    # process, not axon itself. Set them in ~/.axon/.env only if you need to
    # override the inline defaults; do not add more axon tuning vars here.
    #   TEI_MAX_CONCURRENT_REQUESTS       (default 512)
    #   TEI_MAX_BATCH_TOKENS              (default 196608)
    #   TEI_MAX_BATCH_REQUESTS            (default 512)
    #   TEI_SERVER_MAX_CLIENT_BATCH_SIZE  (default 128)
    #   TEI_POOLING                       (default last-token)
    #   TEI_TOKENIZATION_WORKERS          (default 20)
    command:
      - --model-id
      - ${TEI_EMBEDDING_MODEL:-Qwen/Qwen3-Embedding-0.6B}
      - --dtype
      - float16
      - --max-concurrent-requests
      - "${TEI_MAX_CONCURRENT_REQUESTS:-512}"
      - --max-batch-tokens
      - "${TEI_MAX_BATCH_TOKENS:-196608}"
      - --max-batch-requests
      - "${TEI_MAX_BATCH_REQUESTS:-512}"
      - --max-client-batch-size
      - "${TEI_SERVER_MAX_CLIENT_BATCH_SIZE:-128}"
      - --pooling
      - ${TEI_POOLING:-last-token}
      - --tokenization-workers
      - "${TEI_TOKENIZATION_WORKERS:-20}"
      - --auto-truncate
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s

  axon-chrome:
    # headless_browser manager + chrome-headless-shell
    # Port 6000: management API (GET / → "healthy", /fork, /shutdown)
    # Port 9222: CDP proxy — pointed at by AXON_CHROME_REMOTE_URL
    # Security: no built-in auth on CDP/headless_browser. Bind the bundled
    # provider to host loopback only; remote deployments must use an explicit
    # authenticated tunnel/proxy or the external-provider overlay.
    <<: *common-service
    container_name: ${AXON_CHROME_CONTAINER_NAME:-axon-chrome}
    build:
      context: ./config
      dockerfile: chrome/Dockerfile
    shm_size: "2gb"
    ports:
      - "127.0.0.1:${AXON_CHROME_CDP_PORT:-9222}:9222"  # CDP proxy
      - "127.0.0.1:${AXON_CHROME_DEVTOOLS_PORT:-9223}:9223"  # raw Chrome DevTools
      - "127.0.0.1:${AXON_CHROME_MANAGEMENT_PORT:-6000}:6000"  # management API
    environment:
      HOSTNAME_OVERRIDE: axon-chrome
    healthcheck:
      test: ["CMD-SHELL", "curl -fsSL --max-time 4 http://127.0.0.1:9222/json/version > /dev/null"]
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 20s

networks:
  axon:
    name: ${DOCKER_NETWORK:-axon}
    external: true