Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
# hiai-docs Docker Compose
# Copy .env.example to .env before running

services:
  postgres:
    build:
      context: ./postgres
    image: hiai-postgres:18-custom
    restart: unless-stopped
    ports:
      - "127.0.0.1:${DB_PORT:-5437}:5432"
    volumes:
      - pgdata:/var/lib/postgresql
      # init.sql and the runtime-role bootstrap live in the build image.
    environment:
      POSTGRES_DB: ${DB_NAME:-hiai_docs}
      POSTGRES_USER: ${DB_USER:-aiuser}
      POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD must be set}
      HIAI_APP_PASSWORD: ${HIAI_APP_PASSWORD:?HIAI_APP_PASSWORD must be set}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-aiuser} -d ${DB_NAME:-hiai_docs}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - hiai-docs-net
    deploy:
      resources:
        limits:
          memory: 2G
          cpus: "2"

  redis:
    image: redis:8-alpine
    restart: unless-stopped
    ports:
      - "127.0.0.1:${REDIS_PORT:-6384}:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - hiai-docs-net
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: "0.5"

  seaweedfs:
    image: chrislusf/seaweedfs:3.85
    restart: unless-stopped
    ports:
      - "${STORAGE_PORT:-50702}:8333"      # S3 Gateway
      - "${STORAGE_UI_BIND_ADDRESS:-127.0.0.1}:${STORAGE_UI_PORT:-50703}:8888"   # operator-only SeaweedFS Filer UI
    volumes:
      - seaweedfs_data:/data
    entrypoint: ["/bin/sh", "-ec"]
    command:
      - |
        printf '%s' "$$SEAWEEDFS_S3_CONFIG" > /data/s3.json
        exec /usr/bin/weed server -s3 -s3.port=8333 -s3.config=/data/s3.json -filer -filer.port=8888 -master.port=9333 -volume.port=8080 -dir=/data -ip=127.0.0.1 -ip.bind=0.0.0.0
    environment:
      SEAWEEDFS_S3_CONFIG: >-
        {"identities":[{"name":"hiai-docs","credentials":[{"accessKey":"${STORAGE_ACCESS_KEY:-hiai-docs}","secretKey":"${STORAGE_SECRET_KEY:?STORAGE_SECRET_KEY must be set}"}],"actions":["Admin","Read","List","Tagging","Write"]}]}
      SEAWEEDFS_S3_CORS_ALLOW_ORIGIN: ${STORAGE_CORS_ALLOW_ORIGIN:-http://localhost:50701,http://127.0.0.1:50701}
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8888/"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - hiai-docs-net
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "1"

  migrate:
    build:
      context: .
      dockerfile: Dockerfile.backend
      target: migration
      network: host
    restart: "no"
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      MIGRATION_DATABASE_URL: postgresql://aiuser:${DB_PASSWORD:?DB_PASSWORD must be set}@postgres:5432/${DB_NAME:-hiai_docs}
    networks:
      - hiai-docs-net

  api:
    image: docsmint-api:local
    build:
      context: .
      dockerfile: Dockerfile.backend
      network: host
    restart: unless-stopped
    ports:
      # API_PORT selects only the published host port. The container contract
      # is always 50700 so service discovery and health checks stay stable.
      - "127.0.0.1:${API_PORT:-50700}:50700"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      postgres:
        condition: service_healthy
      migrate:
        condition: service_completed_successfully
      redis:
        condition: service_healthy
      seaweedfs:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:50700/api/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 10s
    environment:
      DATABASE_URL: postgresql://hiai_app:${HIAI_APP_PASSWORD:?HIAI_APP_PASSWORD must be set}@postgres:5432/${DB_NAME:-hiai_docs}
      HIAI_APP_PASSWORD: ${HIAI_APP_PASSWORD:?HIAI_APP_PASSWORD must be set}
      REDIS_URL: ${REDIS_URL:-redis://redis:6379}
      STORAGE_ENDPOINT: ${STORAGE_ENDPOINT:-seaweedfs}
      STORAGE_PORT: 8333
      STORAGE_INTERNAL_ENDPOINT_URL: ${STORAGE_INTERNAL_ENDPOINT_URL:-http://seaweedfs:8333}
      STORAGE_ACCESS_KEY: ${STORAGE_ACCESS_KEY:-hiai-docs}
      STORAGE_SECRET_KEY: ${STORAGE_SECRET_KEY:?STORAGE_SECRET_KEY must be set}
      STORAGE_BUCKET: ${STORAGE_BUCKET:-hiai-docs}
      STORAGE_PUBLIC_ENDPOINT: ${STORAGE_PUBLIC_ENDPOINT:-localhost}
      STORAGE_PUBLIC_PORT: ${STORAGE_PORT:-50702}
      STORAGE_PUBLIC_ENDPOINT_URL: ${STORAGE_PUBLIC_ENDPOINT_URL:-http://localhost:${STORAGE_PORT:-50702}}
      STORAGE_REGION: ${STORAGE_REGION:-us-east-1}
      STORAGE_FORCE_PATH_STYLE: ${STORAGE_FORCE_PATH_STYLE:-true}
      BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}
      BETTER_AUTH_URL: ${BETTER_AUTH_URL:-http://localhost:${API_PORT:-50700}}
      # Custom host WEB_PORT mappings require an explicit CORS_ORIGINS value.
      CORS_ORIGINS: ${CORS_ORIGINS:-}
      PUBLIC_APP_URL: ${PUBLIC_APP_URL:-}
      MCP_HTTP_ENABLED: ${MCP_HTTP_ENABLED:-true}
      TRUSTED_ORIGINS: ${TRUSTED_ORIGINS:-}
      # OpenRouter is the public default; override these three values for
      # Ollama or another OpenAI-compatible embedding service.
      OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
      EMBEDDING_BASE_URL: ${EMBEDDING_BASE_URL:-https://openrouter.ai/api/v1}
      EMBEDDING_API_KEY: ${EMBEDDING_API_KEY:-}
      EMBEDDING_MODEL: ${EMBEDDING_MODEL:-voyageai/voyage-4-lite}
      EMBEDDING_TIMEOUT_MS: ${EMBEDDING_TIMEOUT_MS:-20000}
      EMBEDDING_FALLBACK_BASE_URL: ${EMBEDDING_FALLBACK_BASE_URL:-https://openrouter.ai/api/v1}
      EMBEDDING_FALLBACK_API_KEY: ${EMBEDDING_FALLBACK_API_KEY:-}
      EMBEDDING_FALLBACK_MODEL: ${EMBEDDING_FALLBACK_MODEL:-baai/bge-m3}
      EMBEDDING_FALLBACK_2_BASE_URL: ${EMBEDDING_FALLBACK_2_BASE_URL:-https://openrouter.ai/api/v1}
      EMBEDDING_FALLBACK_2_API_KEY: ${EMBEDDING_FALLBACK_2_API_KEY:-}
      EMBEDDING_FALLBACK_2_MODEL: ${EMBEDDING_FALLBACK_2_MODEL:-openai/text-embedding-3-small}
      API_PORT: 50700
      HIAI_DOCS_API_KEY: ${HIAI_DOCS_API_KEY}
      API_KEY_ENCRYPTION_SECRET: ${API_KEY_ENCRYPTION_SECRET:?API_KEY_ENCRYPTION_SECRET must be set}
      CSRF_SECRET: ${CSRF_SECRET:?CSRF_SECRET must be set}
      WEBHOOK_SECRET: ${WEBHOOK_SECRET:?WEBHOOK_SECRET must be set}
      OWNER_ID: ${OWNER_ID}
      DOCSMINT_WORKSPACE_ENABLED: ${DOCSMINT_WORKSPACE_ENABLED:-false}
      DOCSMINT_WORKSPACE_ISSUER: ${DOCSMINT_WORKSPACE_ISSUER:-}
      DOCSMINT_WORKSPACE_SECRET: ${DOCSMINT_WORKSPACE_SECRET:-}
      DOCSMINT_WORKSPACE_CLOCK_SKEW_SECONDS: ${DOCSMINT_WORKSPACE_CLOCK_SKEW_SECONDS:-5}
      EMBEDDING_OLLAMA_URL: ${EMBEDDING_OLLAMA_URL:-}
      EMBEDDING_FALLBACK_PROVIDER: ${EMBEDDING_FALLBACK_PROVIDER:-}
      GRAPH_EXTRACT_ENABLED: ${GRAPH_EXTRACT_ENABLED:-true}
      GRAPH_SEARCH_ENABLED: ${GRAPH_SEARCH_ENABLED:-true}
      GRAPH_EXTRACT_BASE_URL: ${GRAPH_EXTRACT_BASE_URL:-https://openrouter.ai/api/v1}
      GRAPH_EXTRACT_API_KEY: ${GRAPH_EXTRACT_API_KEY:-}
      GRAPH_EXTRACT_MODEL: ${GRAPH_EXTRACT_MODEL:-nvidia/nemotron-3.5-lightning}
      GRAPH_EXTRACT_REASONING_EFFORT: ${GRAPH_EXTRACT_REASONING_EFFORT:-}
      GRAPH_EXTRACT_TIMEOUT_MS: ${GRAPH_EXTRACT_TIMEOUT_MS:-30000}
      GRAPH_EXTRACT_FALLBACK_BASE_URL: ${GRAPH_EXTRACT_FALLBACK_BASE_URL:-https://openrouter.ai/api/v1}
      GRAPH_EXTRACT_FALLBACK_API_KEY: ${GRAPH_EXTRACT_FALLBACK_API_KEY:-}
      GRAPH_EXTRACT_FALLBACK_MODEL: ${GRAPH_EXTRACT_FALLBACK_MODEL:-mistralai/mistral-small-2603}
      GRAPH_EXTRACT_FALLBACK_2_BASE_URL: ${GRAPH_EXTRACT_FALLBACK_2_BASE_URL:-https://openrouter.ai/api/v1}
      GRAPH_EXTRACT_FALLBACK_2_API_KEY: ${GRAPH_EXTRACT_FALLBACK_2_API_KEY:-}
      GRAPH_EXTRACT_FALLBACK_2_MODEL: ${GRAPH_EXTRACT_FALLBACK_2_MODEL:-google/gemma-4-31b-it}
      GRAPH_EXTRACT_MIN_CONFIDENCE: ${GRAPH_EXTRACT_MIN_CONFIDENCE:-0.5}
      SEARCH_EXPANSION_ENABLED: ${SEARCH_EXPANSION_ENABLED:-true}
      SEARCH_EXPANSION_BASE_URL: ${SEARCH_EXPANSION_BASE_URL:-https://openrouter.ai/api/v1}
      SEARCH_EXPANSION_API_KEY: ${SEARCH_EXPANSION_API_KEY:-}
      SEARCH_EXPANSION_MODEL: ${SEARCH_EXPANSION_MODEL:-poolside/laguna-xs-2.1}
      SEARCH_EXPANSION_FALLBACK_BASE_URL: ${SEARCH_EXPANSION_FALLBACK_BASE_URL:-https://openrouter.ai/api/v1}
      SEARCH_EXPANSION_FALLBACK_API_KEY: ${SEARCH_EXPANSION_FALLBACK_API_KEY:-}
      SEARCH_EXPANSION_FALLBACK_MODEL: ${SEARCH_EXPANSION_FALLBACK_MODEL:-openai/gpt-5.6-luna}
      SEARCH_EXPANSION_FALLBACK_2_BASE_URL: ${SEARCH_EXPANSION_FALLBACK_2_BASE_URL:-https://openrouter.ai/api/v1}
      SEARCH_EXPANSION_FALLBACK_2_API_KEY: ${SEARCH_EXPANSION_FALLBACK_2_API_KEY:-}
      SEARCH_EXPANSION_FALLBACK_2_MODEL: ${SEARCH_EXPANSION_FALLBACK_2_MODEL:-google/gemini-3.5-flash-lite}
      SEARCH_RERANK_ENABLED: ${SEARCH_RERANK_ENABLED:-true}
      SEARCH_RERANK_BASE_URL: ${SEARCH_RERANK_BASE_URL:-https://openrouter.ai/api/v1}
      SEARCH_RERANK_API_KEY: ${SEARCH_RERANK_API_KEY:-}
      SEARCH_RERANK_MODEL: ${SEARCH_RERANK_MODEL:-voyageai/rerank-2.5}
      SEARCH_RERANK_FALLBACK_BASE_URL: ${SEARCH_RERANK_FALLBACK_BASE_URL:-https://openrouter.ai/api/v1}
      SEARCH_RERANK_FALLBACK_API_KEY: ${SEARCH_RERANK_FALLBACK_API_KEY:-}
      SEARCH_RERANK_FALLBACK_MODEL: ${SEARCH_RERANK_FALLBACK_MODEL:-cohere/rerank-v3.5}
      SEARCH_RERANK_FALLBACK_2_BASE_URL: ${SEARCH_RERANK_FALLBACK_2_BASE_URL:-https://openrouter.ai/api/v1}
      SEARCH_RERANK_FALLBACK_2_API_KEY: ${SEARCH_RERANK_FALLBACK_2_API_KEY:-}
      SEARCH_RERANK_FALLBACK_2_MODEL: ${SEARCH_RERANK_FALLBACK_2_MODEL:-nvidia/llama-nemotron-rerank-vl-1b-v2:free}
      SEARCH_RERANK_TIMEOUT_MS: ${SEARCH_RERANK_TIMEOUT_MS:-3000}
      SEARCH_RERANK_TOP_N: ${SEARCH_RERANK_TOP_N:-20}
      SEARCH_RERANK_MAX_CHARS: ${SEARCH_RERANK_MAX_CHARS:-1500}
      SEARCH_RERANK_GRAPH_POSITION: ${SEARCH_RERANK_GRAPH_POSITION:-after}
      SEARCH_EXPANSION_TIMEOUT_MS: ${SEARCH_EXPANSION_TIMEOUT_MS:-6000}
      SEARCH_VECTOR_PROVIDER_TIMEOUT_MS: ${SEARCH_VECTOR_PROVIDER_TIMEOUT_MS:-2500}
      SEARCH_EXPANSION_CACHE_TTL_SECONDS: ${SEARCH_EXPANSION_CACHE_TTL_SECONDS:-86400}
      SEARCH_EXPANSION_MAX_VARIANTS: ${SEARCH_EXPANSION_MAX_VARIANTS:-12}
      SEARCH_EXPANSION_ESTIMATED_COST_MICROUNITS: ${SEARCH_EXPANSION_ESTIMATED_COST_MICROUNITS:-0}
      SEARCH_RRF_K: ${SEARCH_RRF_K:-60}
      SEARCH_EXACT_BOOST: ${SEARCH_EXACT_BOOST:-0.02}
      SEARCH_CHANNEL_AGREEMENT_BOOST: ${SEARCH_CHANNEL_AGREEMENT_BOOST:-0.01}
      SEARCH_GRAPH_MAX_CONTRIBUTION: ${SEARCH_GRAPH_MAX_CONTRIBUTION:-0.03}
      SEARCH_VECTOR_MIN_SIMILARITY: ${SEARCH_VECTOR_MIN_SIMILARITY:-0.35}
      SEARCH_FUZZY_MIN_SIMILARITY: ${SEARCH_FUZZY_MIN_SIMILARITY:-0.25}
      SEARCH_MIN_CHANNEL_AGREEMENT: ${SEARCH_MIN_CHANNEL_AGREEMENT:-2}
      SEARCH_GRAPH_SEED_LIMIT: ${SEARCH_GRAPH_SEED_LIMIT:-10}
      SEARCH_GRAPH_MAX_HOPS: ${SEARCH_GRAPH_MAX_HOPS:-2}
      SEARCH_GRAPH_RESULT_LIMIT: ${SEARCH_GRAPH_RESULT_LIMIT:-20}
      HYBRID_TEXT_WEIGHT: ${HYBRID_TEXT_WEIGHT:-0.4}
      HYBRID_SEMANTIC_WEIGHT: ${HYBRID_SEMANTIC_WEIGHT:-0.6}
      CHUNK_TARGET_TOKENS: ${CHUNK_TARGET_TOKENS:-500}
      CHUNK_OVERLAP_TOKENS: ${CHUNK_OVERLAP_TOKENS:-50}
      FOLDER_REEMBED_BATCH_SIZE: ${FOLDER_REEMBED_BATCH_SIZE:-100}
      CATEGORY_REEMBED_BATCH_SIZE: ${CATEGORY_REEMBED_BATCH_SIZE:-100}
      TAG_REEMBED_BATCH_SIZE: ${TAG_REEMBED_BATCH_SIZE:-500}
      AI_PROVIDER: ${AI_PROVIDER:-openrouter}
      OLLAMA_PORT: ${OLLAMA_PORT:-11434}
      QUEUE_PREPARE_CONCURRENCY: ${QUEUE_PREPARE_CONCURRENCY:-2}
      QUEUE_EMBED_CONCURRENCY: ${QUEUE_EMBED_CONCURRENCY:-3}
      QUEUE_GRAPH_CONCURRENCY: ${QUEUE_GRAPH_CONCURRENCY:-2}
      QUEUE_SUMMARY_CONCURRENCY: ${QUEUE_SUMMARY_CONCURRENCY:-1}
      QUEUE_FINALIZE_CONCURRENCY: ${QUEUE_FINALIZE_CONCURRENCY:-2}
      QUEUE_EMBED_BATCH_SIZE: ${QUEUE_EMBED_BATCH_SIZE:-5}
      QUEUE_MAX_ACTIVE_BATCHES_PER_DOCUMENT: ${QUEUE_MAX_ACTIVE_BATCHES_PER_DOCUMENT:-2}
      QUEUE_MAX_ACTIVE_PREPARE_PER_OWNER: ${QUEUE_MAX_ACTIVE_PREPARE_PER_OWNER:-2}
      QUEUE_MAX_ACTIVE_EMBED_PER_OWNER: ${QUEUE_MAX_ACTIVE_EMBED_PER_OWNER:-4}
      QUEUE_MAX_ACTIVE_GRAPH_PER_OWNER: ${QUEUE_MAX_ACTIVE_GRAPH_PER_OWNER:-1}
      QUEUE_JOB_ATTEMPTS: ${QUEUE_JOB_ATTEMPTS:-5}
      QUEUE_RETRY_BASE_DELAY_MS: ${QUEUE_RETRY_BASE_DELAY_MS:-1000}
      QUEUE_RECOVERY_STALE_AFTER_MS: ${QUEUE_RECOVERY_STALE_AFTER_MS:-120000}
      QUEUE_COMPLETED_RETENTION_COUNT: ${QUEUE_COMPLETED_RETENTION_COUNT:-1000}
      QUEUE_FAILED_RETENTION_COUNT: ${QUEUE_FAILED_RETENTION_COUNT:-5000}
      QUEUE_SHUTDOWN_GRACE_MS: ${QUEUE_SHUTDOWN_GRACE_MS:-30000}
      PROVIDER_LIMITER_MODE: ${PROVIDER_LIMITER_MODE:-remote}
      PROVIDER_MAX_CONCURRENCY: ${PROVIDER_MAX_CONCURRENCY:-4}
      PROVIDER_REQUESTS_PER_MINUTE: ${PROVIDER_REQUESTS_PER_MINUTE:-0}
      PROVIDER_MAX_RETRIES: ${PROVIDER_MAX_RETRIES:-3}
      PROVIDER_RETRY_BASE_DELAY_MS: ${PROVIDER_RETRY_BASE_DELAY_MS:-250}
      PROVIDER_CIRCUIT_FAILURE_THRESHOLD: ${PROVIDER_CIRCUIT_FAILURE_THRESHOLD:-5}
      PROVIDER_CIRCUIT_COOLDOWN_MS: ${PROVIDER_CIRCUIT_COOLDOWN_MS:-30000}
      NODE_ENV: ${NODE_ENV:-production}
      LOG_LEVEL: ${LOG_LEVEL:-info}
    networks:
      - hiai-docs-net
    deploy:
      resources:
        limits:
          memory: 512M

  web:
    image: docsmint-web:local
    build:
      context: .
      dockerfile: frontend/Dockerfile
      network: host
      args:
        PUBLIC_APP_ID: ${PUBLIC_APP_ID:-docsmint}
        PUBLIC_DEPLOYMENT_ID: ${PUBLIC_DEPLOYMENT_ID:-docsmint-oss-2.1.5}
    restart: unless-stopped
    ports:
      # WEB_PORT selects only the published host port. The container contract
      # is always 50701.
      - "${WEB_PORT:-50701}:50701"
    depends_on:
      api:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:50701/ || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 10s
    environment:
      API_URL: http://api:50700
      PORT: 50701
      WEB_PORT: 50701
      # adapter-node defaults to 512 KiB. Keep the proxy envelope aligned
      # with the API's 100 MiB global body cap; the API still enforces the
      # 10 MiB per-file import limit.
      BODY_SIZE_LIMIT: "104857600"
    networks:
      - hiai-docs-net
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "1"

  caddy:
    image: docsmint-caddy:local
    build:
      context: .
      dockerfile: Dockerfile.caddy
    restart: unless-stopped
    profiles:
      - caddy
    ports:
      - "${CADDY_HTTP_PORT:-80}:80"
      - "${CADDY_HTTPS_PORT:-443}:443"
    depends_on:
      api:
        condition: service_healthy
      web:
        condition: service_healthy
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - hiai-docs-net
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: "0.5"

networks:
  hiai-docs-net:
    driver: bridge

volumes:
  pgdata:
  # AGE graph now lives in the unified postgres database.
  redis_data:
  seaweedfs_data:
  caddy_data:
  caddy_config: