Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
# =============================================================================
# Transfer-Station — 一键部署 AI API 中转站
# =============================================================================
# 要求: Docker Compose V2 (docker compose, 非 docker-compose)
# 注意: deploy.resources 内存限制需要 Docker Engine 20.10+ 和 Compose 2.4+
#       如果你的环境不支持,删除 deploy 段即可,不影响功能
# =============================================================================
# 快速开始:
#   1. ./setup.sh            (生成 .env + nginx 配置)
#   2. docker compose up -d  (拉起全部服务)
#   3. 浏览器打开 https://你的域名
#
# 可选聊天 UI:
#   docker compose --profile chat up -d
# =============================================================================

services:
  # ---------------------------------------------------------------------------
  # Sub2API — AI 网关 / 号池调度 / 计费 / 支付
  # ---------------------------------------------------------------------------
  sub2api:
    image: weishaw/sub2api:latest
    container_name: sub2api
    restart: unless-stopped
    ulimits:
      nofile: { soft: 100000, hard: 100000 }
    ports:
      - "127.0.0.1:${SUB2API_PORT:-8080}:8080"
    volumes:
      - ./data/sub2api:/app/data
    environment:
      - AUTO_SETUP=true
      - SERVER_HOST=0.0.0.0
      - SERVER_PORT=8080
      - SERVER_MODE=${SERVER_MODE:-release}
      - RUN_MODE=${RUN_MODE:-standard}
      # 数据库
      - DATABASE_HOST=postgres
      - DATABASE_PORT=5432
      - DATABASE_USER=${POSTGRES_USER:-sub2api}
      - DATABASE_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
      - DATABASE_DBNAME=${POSTGRES_DB:-sub2api}
      - DATABASE_SSLMODE=disable
      - DATABASE_MAX_OPEN_CONNS=${DATABASE_MAX_OPEN_CONNS:-50}
      - DATABASE_MAX_IDLE_CONNS=${DATABASE_MAX_IDLE_CONNS:-10}
      # Redis
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=${REDIS_PASSWORD:-}
      - REDIS_DB=0
      # 管理员
      - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
      - ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
      # JWT / TOTP — 必须固定,否则重启后用户全掉线
      - JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required}
      - JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24}
      - TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY:?TOTP_ENCRYPTION_KEY is required}
      # 时区
      - TZ=${TZ:-Asia/Shanghai}
      # Gemini OAuth(可选)
      - GEMINI_OAUTH_CLIENT_ID=${GEMINI_OAUTH_CLIENT_ID:-}
      - GEMINI_OAUTH_CLIENT_SECRET=${GEMINI_OAUTH_CLIENT_SECRET:-}
      # 图片生成
      - GATEWAY_IMAGE_STREAM_DATA_INTERVAL_TIMEOUT=${GATEWAY_IMAGE_STREAM_DATA_INTERVAL_TIMEOUT:-900}
      - GATEWAY_IMAGE_STREAM_KEEPALIVE_INTERVAL=${GATEWAY_IMAGE_STREAM_KEEPALIVE_INTERVAL:-10}
    depends_on:
      postgres: { condition: service_healthy }
      redis:    { condition: service_healthy }
    networks: [internal]
    healthcheck:
      test: ["CMD", "wget", "-q", "-T", "5", "-O", "/dev/null", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    deploy:
      resources:
        limits:   { memory: 280M }
        reservations: { memory: 100M }

  # ---------------------------------------------------------------------------
  # PostgreSQL
  # ---------------------------------------------------------------------------
  postgres:
    image: postgres:18-alpine
    container_name: sub2api-postgres
    restart: unless-stopped
    ulimits:
      nofile: { soft: 100000, hard: 100000 }
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=${POSTGRES_USER:-sub2api}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
      - POSTGRES_DB=${POSTGRES_DB:-sub2api}
      - PGDATA=/var/lib/postgresql/data
      - TZ=${TZ:-Asia/Shanghai}
    networks: [internal]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-sub2api} -d ${POSTGRES_DB:-sub2api}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    deploy:
      resources:
        limits:   { memory: 220M }
        reservations: { memory: 80M }

  # ---------------------------------------------------------------------------
  # Redis
  # ---------------------------------------------------------------------------
  redis:
    image: redis:8-alpine
    container_name: sub2api-redis
    restart: unless-stopped
    ulimits:
      nofile: { soft: 100000, hard: 100000 }
    volumes:
      - ./data/redis:/data
    command: >
      sh -c 'redis-server
        --save 60 1
        --appendonly yes
        --appendfsync everysec
        ${REDIS_PASSWORD:+--requirepass "$REDIS_PASSWORD"}'
    environment:
      - TZ=${TZ:-Asia/Shanghai}
      - REDISCLI_AUTH=${REDIS_PASSWORD:-}
    networks: [internal]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 5s
    deploy:
      resources:
        limits:   { memory: 60M }
        reservations: { memory: 20M }

  # ---------------------------------------------------------------------------
  # Hermes Web UI — 可选聊天前端 (docker compose --profile chat up -d)
  # ---------------------------------------------------------------------------
  hermes-webui:
    image: ekkoye8888/hermes-web-ui:latest
    container_name: hermes-webui
    restart: unless-stopped
    ports:
      - "127.0.0.1:${HERMES_PORT:-6060}:6060"
    environment:
      - TZ=${TZ:-Asia/Shanghai}
    profiles: [chat]
    networks: [internal]
    deploy:
      resources:
        limits: { memory: 200M }

networks:
  internal:
    driver: bridge