# ============================================================================= # Sub2API - Docker Compose Configuration (All-in-One) # ============================================================================= # 包含服务: # - sub2api 主应用 # - postgres 数据库 # - redis 缓存 # - windsurf-ls 可选: Windsurf Language Server (通过 profile 启用) # # 启动方式: # 默认三件套: docker compose up -d # 含 Windsurf LS: docker compose --profile windsurf up -d # # 注意: # - 首次启动前先复制 .env.example 为 .env 并填写必填变量 # - JWT_SECRET / TOTP_ENCRYPTION_KEY 多实例必须固定 # - windsurf-ls 镜像仅支持 linux/amd64, arm64 宿主机会通过 QEMU 模拟 # ============================================================================= services: # =========================================================================== # Sub2API Application # =========================================================================== sub2api: image: docker.io/zfc931912343/sub2api:latest container_name: sub2api restart: unless-stopped ulimits: nofile: soft: 100000 hard: 100000 ports: - "0.0.0.0:80:8080" volumes: - sub2api_data:/app/data # Optional: 挂载自定义 config.yaml(先从 config.example.yaml 复制并修改) # - ./config.yaml:/app/data/config.yaml # Optional: 自定义 Codex instructions 模板 # - ./codex-instructions.md.tmpl:/app/data/codex-instructions.md.tmpl:ro environment: - AUTO_SETUP=true # --- Server --- - SERVER_HOST=0.0.0.0 - SERVER_PORT=8080 - SERVER_MODE=${SERVER_MODE:-release} - RUN_MODE=${RUN_MODE:-standard} # --- Database (PostgreSQL) --- - 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} - DATABASE_CONN_MAX_LIFETIME_MINUTES=${DATABASE_CONN_MAX_LIFETIME_MINUTES:-30} - DATABASE_CONN_MAX_IDLE_TIME_MINUTES=${DATABASE_CONN_MAX_IDLE_TIME_MINUTES:-5} # --- Redis --- - REDIS_HOST=redis - REDIS_PORT=6379 - REDIS_PASSWORD=${REDIS_PASSWORD:-} - REDIS_DB=${REDIS_DB:-0} - REDIS_POOL_SIZE=${REDIS_POOL_SIZE:-1024} - REDIS_MIN_IDLE_CONNS=${REDIS_MIN_IDLE_CONNS:-10} - REDIS_ENABLE_TLS=${REDIS_ENABLE_TLS:-false} # --- Admin(仅首次启动生效)--- - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@sub2api.local} - ADMIN_PASSWORD=${ADMIN_PASSWORD:-} # --- JWT(多实例必须固定,否则重启后 session 失效)--- # 生成: openssl rand -hex 32 - JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required for multi-instance} - JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24} # --- TOTP 2FA(多实例必须固定,否则 2FA 失效)--- # 生成: openssl rand -hex 32 - TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY:?TOTP_ENCRYPTION_KEY is required for multi-instance} # --- Timezone --- - TZ=${TZ:-Asia/Shanghai} # --- Gemini OAuth --- - GEMINI_OAUTH_CLIENT_ID=${GEMINI_OAUTH_CLIENT_ID:-} - GEMINI_OAUTH_CLIENT_SECRET=${GEMINI_OAUTH_CLIENT_SECRET:-} - GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-} - GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-} - GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-} - ANTIGRAVITY_OAUTH_CLIENT_SECRET=${ANTIGRAVITY_OAUTH_CLIENT_SECRET:-} # --- Security --- - SECURITY_URL_ALLOWLIST_ENABLED=${SECURITY_URL_ALLOWLIST_ENABLED:-false} - SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=${SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP:-false} - SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS=${SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS:-false} - SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS=${SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS:-} # --- Update Proxy(国内机器可配置代理访问 GitHub)--- - UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-} # --- Windsurf (账号管理/登录,不依赖 LS) --- - WINDSURF_ENABLED=${WINDSURF_ENABLED:-false} - WINDSURF_FIREBASE_API_KEY=${WINDSURF_FIREBASE_API_KEY:-} # --- Windsurf Language Server (可选,启用 windsurf profile 时生效) --- - WINDSURF_DOCKER_HOST=${WINDSURF_DOCKER_HOST:-windsurf-ls} - WINDSURF_DOCKER_PORT=${WINDSURF_DOCKER_PORT:-42099} - WINDSURF_DOCKER_CSRF_TOKEN=${WINDSURF_DOCKER_CSRF_TOKEN:-} depends_on: postgres: condition: service_healthy redis: condition: service_healthy windsurf-ls: condition: service_healthy required: false networks: - sub2api-network healthcheck: test: [ "CMD", "wget", "-q", "-T", "5", "-O", "/dev/null", "http://localhost:8080/health" ] interval: 30s timeout: 10s retries: 3 start_period: 30s # =========================================================================== # PostgreSQL Database # =========================================================================== postgres: image: postgres:18-alpine container_name: sub2api-postgres restart: unless-stopped ulimits: nofile: soft: 100000 hard: 100000 volumes: - postgres_data:/var/lib/postgresql/data environment: # postgres:18-alpine 默认 PGDATA 在镜像内部匿名卷,必须显式指定才能持久化到命名卷 # 与旧版 compose 保持一致: PGDATA=/var/lib/postgresql/data,迁移无感 - PGDATA=/var/lib/postgresql/data - POSTGRES_USER=${POSTGRES_USER:-sub2api} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} - POSTGRES_DB=${POSTGRES_DB:-sub2api} - TZ=${TZ:-Asia/Shanghai} # 默认不对外暴露端口。如需从宿主机调试,取消注释: # ports: # - "127.0.0.1:5433:5432" networks: - sub2api-network healthcheck: test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-sub2api} -d ${POSTGRES_DB:-sub2api}" ] interval: 10s timeout: 5s retries: 5 start_period: 15s # =========================================================================== # Redis Cache # =========================================================================== redis: image: redis:8-alpine container_name: sub2api-redis restart: unless-stopped ulimits: nofile: soft: 100000 hard: 100000 volumes: - redis_data:/data command: > sh -c 'if [ -n "$$REDIS_PASSWORD" ]; then exec redis-server --requirepass "$$REDIS_PASSWORD" --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru; else exec redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru; fi' environment: - REDIS_PASSWORD=${REDIS_PASSWORD:-} # 默认不对外暴露端口。如需调试取消注释: # ports: # - "127.0.0.1:6380:6379" networks: - sub2api-network healthcheck: test: [ "CMD-SHELL", "if [ -n \"$$REDIS_PASSWORD\" ]; then redis-cli -a \"$$REDIS_PASSWORD\" ping; else redis-cli ping; fi" ] interval: 10s timeout: 5s retries: 5 start_period: 10s # =========================================================================== # Windsurf Language Server (可选) # --------------------------------------------------------------------------- # 启用方式: docker compose --profile windsurf up -d # # 架构说明: # - LS 本体只能绑 127.0.0.1:LS_INTERNAL_PORT(CSRF 仅允许 loopback peer)。 # - 容器内 socat 把 0.0.0.0:42099 → 127.0.0.1:42099, # 使 compose 同网络内的服务可直接通过 `windsurf-ls:42099` 访问。 # # 架构限制: 官方 LS 二进制仅提供 linux/amd64 & linux/arm64;非匹配平台通过 QEMU 模拟。 # 资源建议: CPU 1-2 cores, Memory 512MB - 1GB # =========================================================================== windsurf-ls: image: docker.io/zfc931912343/sub2api-windsurf-ls:latest container_name: sub2api-windsurf-ls restart: unless-stopped profiles: [ "windsurf" ] volumes: - windsurf_ls_data:/data environment: # Dockerfile.ls 的 ENTRYPOINT 消费这些键,不要改名 # LS_PORT : 容器对外暴露端口(socat 监听) # LS_INTERNAL_PORT: LS 本体监听端口(socat 转发目的端口);两者必须不同 - LS_PORT=42099 - LS_INTERNAL_PORT=42098 - LS_CSRF_TOKEN=${WINDSURF_DOCKER_CSRF_TOKEN:?WINDSURF_DOCKER_CSRF_TOKEN is required when windsurf profile is enabled} - LS_API_SERVER_URL=${LS_API_SERVER_URL:-https://server.self-serve.windsurf.com} - HTTPS_PROXY=${LS_HTTPS_PROXY:-} - HTTP_PROXY=${LS_HTTP_PROXY:-} - TZ=${TZ:-Asia/Shanghai} # 默认不对外暴露端口,仅通过内部网络通信 # 如需从宿主机调试,取消注释: # ports: # - "127.0.0.1:42099:42099" networks: - sub2api-network healthcheck: test: [ "CMD", "nc", "-z", "127.0.0.1", "42099" ] interval: 10s timeout: 3s retries: 5 start_period: 20s networks: sub2api-network: name: sub2api-network driver: bridge volumes: sub2api_data: name: sub2api_data postgres_data: name: sub2api_postgres_data redis_data: name: sub2api_redis_data windsurf_ls_data: name: sub2api_windsurf_ls_data