Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
# ══════════════════════════════════════════════════════════════════════
#  Geny — Docker Compose (Standalone / Full)
#
#  전체 서비스 포함 (로컬 오디오 엔진 TTS/STT 는 --profile audio-local 로 활성화)
#
#  Usage:
#    docker compose up --build              # 기본 실행 (TTS 미포함)
#    docker compose up -d                   # 백그라운드 실행
#    docker compose --profile audio-local up  # 로컬 오디오 엔진 (TTS + STT) 포함
#    docker compose down                    # 종료
#    docker compose logs -f                 # 로그 확인
#
#  Port 변경:
#    BACKEND_PORT=8080 FRONTEND_PORT=3001 docker compose up --build
#
#  또는 .env 파일로 설정:
#    BACKEND_PORT=8080
#    FRONTEND_PORT=3001
#

# ══════════════════════════════════════════════════════════════════════

services:

  # ── PostgreSQL Database ───────────────────────────────────────────
  postgres:
    image: postgres:16-alpine
    container_name: geny-postgres
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-geny}
      POSTGRES_USER: ${POSTGRES_USER:-geny}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geny123}
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
      TZ: ${TIMEZONE:-Asia/Seoul}
    volumes:
      - geny-pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-geny} -d ${POSTGRES_DB:-geny}"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    networks:
      - geny-net

  # ── Backend (FastAPI + LangGraph + Claude CLI) ────────────────────
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: geny-backend
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${BACKEND_PORT:-8000}:8000"
    env_file:
      - path: ./backend/.env
        required: false
    environment:
      - APP_HOST=0.0.0.0
      - APP_PORT=8000
      - GENY_AGENT_STORAGE_ROOT=/data/geny_agent_sessions
      # PostgreSQL
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - POSTGRES_DB=${POSTGRES_DB:-geny}
      - POSTGRES_USER=${POSTGRES_USER:-geny}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-geny123}
      - AUTO_MIGRATION=true
      - TIMEZONE=${TIMEZONE:-Asia/Seoul}
      # Inbound chat gateway (geny-executor >=2.11.0). Set the token in .env
      # to let users DM the VTuber from Telegram; empty = off.
      - GATEWAY_TELEGRAM_BOT_TOKEN=${GATEWAY_TELEGRAM_BOT_TOKEN:-}
      - GATEWAY_TELEGRAM_ALLOWED_CHAT_IDS=${GATEWAY_TELEGRAM_ALLOWED_CHAT_IDS:-}
      - GATEWAY_DISCORD_BOT_TOKEN=${GATEWAY_DISCORD_BOT_TOKEN:-}
      - GATEWAY_DISCORD_ALLOWED_CHANNEL_IDS=${GATEWAY_DISCORD_ALLOWED_CHANNEL_IDS:-}
      - GATEWAY_SLACK_APP_TOKEN=${GATEWAY_SLACK_APP_TOKEN:-}
      - GATEWAY_SLACK_BOT_TOKEN=${GATEWAY_SLACK_BOT_TOKEN:-}
      - GATEWAY_SLACK_ALLOWED_CHANNEL_IDS=${GATEWAY_SLACK_ALLOWED_CHANNEL_IDS:-}
      # ── Persistent-storage paths (Phase 1, 2026-05-19) ─────────────
      # Mirrors docker-compose.prod.yml — each path is backed by a
      # named volume so its data survives ``docker compose up --build``.
      - ENVIRONMENT_STORAGE_PATH=${ENVIRONMENT_STORAGE_PATH:-/data/environments}
      - TRIGGER_PRESET_STORAGE_PATH=${TRIGGER_PRESET_STORAGE_PATH:-/data/trigger_presets}
      - GENY_TOOL_PRESETS_DIR=/data/tool_presets
      - GENY_CHAT_CONVERSATIONS_DIR=/data/chat_conversations
      - GENY_GAME_STATE_DB=/data/creature_state/geny_state.sqlite3
      - GENY_CONFIG_VARIABLES_DIR=/data/config_variables
      - GENY_MCP_CREDENTIALS=/data/mcp/credentials.json
      - GENY_VOICE_STUDIO_DATA_DIR=/data/voice_studio
    volumes:
      - geny-tts-cache:/app/cache/tts
      - geny-voices:/app/static/voices
      # ── Backend storage root (notes / vault / capture logs) ────────
      - geny-agent-sessions:/data/geny_agent_sessions
      # ── Phase-1 persistence volumes (2026-05-19) ───────────────────
      - geny-environments:/data/environments
      - geny-trigger-presets:/data/trigger_presets
      - geny-tool-presets:/data/tool_presets
      - geny-chat-conversations:/data/chat_conversations
      - geny-creature-state:/data/creature_state
      - geny-config-variables:/data/config_variables
      - geny-mcp-credentials:/data/mcp
      - geny-voice-studio:/data/voice_studio
      # Chat attachment uploads (content-addressable: /app/static/uploads/{shard}/{sha256}.{ext}).
      # Cycle 20260424_3 PR-1: without this volume the folder was ephemeral —
      # every container restart wiped user-uploaded images.
      - geny-uploads:/app/static/uploads
      # Baked puppet zips dropped here by the avatar-editor service. Backend
      # lists pending zips and "installs" them into static/{live2d,spine}-models/,
      # then moves the consumed zip into installed/ — rw mount required.
      # See docs/plan/GENY_AVATAR_INTEGRATION.md (Phase C).
      - geny-baked-exports:/data/baked-imports
      # Phase G — host's ``~/.claude`` for Pro/Max OAuth credential
      # passthrough to the in-container ``claude`` CLI. RW for token
      # refresh.
      - ${CLAUDE_HOST_DIR:-geny-claude-creds}:/root/.claude:rw
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 15s
    networks:
      - geny-net

  # ── Frontend (Next.js) ────────────────────────────────────────────
  frontend:
    build:
      # Root context: the page renders shared/chat, which lives beside
      # frontend/ and must be in the image.
      context: .
      dockerfile: frontend/Dockerfile
      args:
        - API_URL=http://backend:8000
    container_name: geny-frontend
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${FRONTEND_PORT:-3000}:3000"
    environment:
      - API_URL=http://backend:8000
      - PORT=3000
      - HOSTNAME=0.0.0.0
    depends_on:
      backend:
        condition: service_healthy
    networks:
      - geny-net

  # ── OmniVoice (Optional — Geny 자체 TTS 마이크로서비스, GPU 필요) ─
  # geny-monorepo 내 omnivoice/ 에서 빌드. 포트 9881, voices 공유 볼륨,
  # HF 모델 캐시는 별도 영구 볼륨. profile: audio-local 로 활성화.
  omnivoice:
    build:
      context: ./omnivoice
      dockerfile: Dockerfile
    image: geny-omnivoice:latest
    container_name: geny-omnivoice
    profiles: ["audio-local"]
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${OMNIVOICE_PORT:-9881}:9881"
    environment:
      - OMNIVOICE_MODEL=${OMNIVOICE_MODEL:-k2-fsa/OmniVoice}
      - OMNIVOICE_DEVICE=${OMNIVOICE_DEVICE:-cuda:0}
      - OMNIVOICE_DTYPE=${OMNIVOICE_DTYPE:-float16}
      # 5070(Blackwell, sm_120) 기준. 공유 GPU 환경에서는 1~2 로 내릴 것.
      - OMNIVOICE_MAX_CONCURRENCY=${OMNIVOICE_MAX_CONCURRENCY:-4}
      - OMNIVOICE_DEFAULT_NUM_STEP=${OMNIVOICE_DEFAULT_NUM_STEP:-16}
      - OMNIVOICE_GPU_MEMORY_FRACTION=${OMNIVOICE_GPU_MEMORY_FRACTION:-0.85}
      - PYTORCH_CUDA_ALLOC_CONF=${PYTORCH_CUDA_ALLOC_CONF:-expandable_segments:True}
      - OMNIVOICE_AUTO_ASR=${OMNIVOICE_AUTO_ASR:-false}
      - OMNIVOICE_LOG_LEVEL=${OMNIVOICE_LOG_LEVEL:-info}
      - HF_ENDPOINT=${HF_ENDPOINT:-https://huggingface.co}
    volumes:
      - geny-voices:/voices:ro
      - geny-omnivoice-models:/models
    shm_size: 8G
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:9881/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 300s
    networks:
      - geny-net

  # ── Avatar Editor (geny-avatar submodule, Next.js) ────────────────
  # Standalone Next.js app from vendor/geny-avatar (pinned to v0.2.0).
  # Lets users edit Spine/Cubism puppets in-browser and "Send to Geny"
  # baked model zips, which land in the geny-baked-exports volume the
  # backend reads from. Standalone compose has no nginx in front, so
  # the editor binds directly to a host port (default 3001).
  #
  # Build context is the submodule path — fresh clones must run
  # `git submodule update --init --recursive` first or the build fails.
  avatar-editor:
    build:
      context: ./vendor/geny-avatar
      dockerfile: Dockerfile
      args:
        # NEXT_PUBLIC_* are inlined at build time; runtime env below is
        # too late. basePath stays empty (root mount, no nginx); GENY_HOST
        # enables the "send to Geny" button + /api/send-to-geny route.
        NEXT_PUBLIC_BASE_PATH: ""
        NEXT_PUBLIC_GENY_HOST: "true"
    container_name: geny-avatar-editor
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${AVATAR_EDITOR_PORT:-3001}:3000"
    environment:
      # Standalone compose has no reverse proxy → avatar-editor served
      # at root, so basePath stays empty (Next.js falls back to "/" routes).
      - NEXT_PUBLIC_BASE_PATH=
      # Enables Geny library auto-sync (lib/sync/genySync) + the
      # /api/library/sync proxy route. Browser-visible flag.
      - NEXT_PUBLIC_GENY_HOST=true
      - GENY_BAKED_EXPORTS_DIR=/exports
      # AI provider keys for the editor's Generate panel.
      - GEMINI_API_KEY=${GEMINI_API_KEY:-}
      - OPENAI_API_KEY=${OPENAI_API_KEY:-}
      - REPLICATE_API_TOKEN=${REPLICATE_API_TOKEN:-}
      - PORT=3000
      - HOSTNAME=0.0.0.0
      - NODE_ENV=production
    volumes:
      # Write side of the shared baked-zip handoff (backend mounts the
      # same volume read-only at /data/baked-imports).
      - geny-baked-exports:/exports
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3000/ || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s
    networks:
      - geny-net


networks:
  geny-net:
    driver: bridge

volumes:
  geny-pgdata:
    driver: local
  geny-tts-cache:
    driver: local
  geny-voices:
    driver: local
  geny-uploads:
    driver: local
  geny-omnivoice-models:
    driver: local
  # Shared between avatar-editor (rw at /exports) and backend (ro at
  # /data/baked-imports). Carries baked puppet zips from the editor's
  # "send to Geny" action to the backend's import flow.
  geny-baked-exports:
    driver: local
  # Phase G — Claude Code OAuth credentials. See backend.volumes.
  geny-claude-creds:
    driver: local
  # ── Backend storage root (notes / vault / capture logs) ───────────
  geny-agent-sessions:
    driver: local
  # ── Phase-1 persistent-storage volumes (2026-05-19) ───────────────
  geny-environments:
    driver: local
  geny-trigger-presets:
    driver: local
  geny-tool-presets:
    driver: local
  geny-chat-conversations:
    driver: local
  geny-creature-state:
    driver: local
  geny-config-variables:
    driver: local
  geny-mcp-credentials:
  geny-voice-studio:
    driver: local