services: windsurf-api: # Pull the prebuilt image by default (released via .github/workflows/release.yml). # `build:` is kept for local dev: `docker compose up --build` rebuilds from source, # and Compose will fall back to building when the image isn't available locally # AND can't be pulled (e.g. before the first stable tag exists, or on a fork). # Overridable so `deploy/update.sh --tag ` can pin a release and # so the updater can retag the previous image for a rollback without # editing this file. Defaults to the moving `latest` tag. image: ${WINDSURFAPI_IMAGE:-ghcr.io/dwgx/windsurf-api:latest} build: context: . dockerfile: Dockerfile args: BUILD_VERSION: ${BUILD_VERSION:-} BUILD_COMMIT: ${BUILD_COMMIT:-} BUILD_COMMIT_MESSAGE: ${BUILD_COMMIT_MESSAGE:-} BUILD_COMMIT_DATE: ${BUILD_COMMIT_DATE:-} BUILD_BRANCH: ${BUILD_BRANCH:-} # Remove container_name to allow Compose to scale replicas restart: unless-stopped init: true env_file: # required:false so a fresh clone (which ships only .env.example, never # .env) can `docker compose up` without aborting on a missing file. The # environment: block below covers the essentials; add a .env to override. - path: .env required: false environment: PORT: 3003 DATA_DIR: /data # DEVIN_CONNECT defaults ON in Docker so a fresh `docker compose up` uses # the pure-HTTP cloud egress + native tool_call path out of the box — # native is what lets weaker models (glm-5.2 etc.) follow the tool # protocol reliably in agentic clients (Claude Code/Cline/Codex). With it # OFF, requests fall back to Cascade + prompt-emulation, where those # models intermittently narrate instead of emitting a tool_call (#210). # `${VAR:-1}` keeps it overridable: set DEVIN_CONNECT=0 in your .env to # force the legacy Cascade path. DEVIN_CONNECT: ${DEVIN_CONNECT:-1} # Bundled nginx LB is the only hop in front of the app in this topology, # so trust its X-Forwarded-For — otherwise every caller looks like the # nginx container IP and the per-caller brute-force lockout collapses to # one global bucket. Exactly one trusted hop here. TRUST_PROXY_X_FORWARDED_FOR: "1" TRUST_PROXY_HOPS: "1" WINDSURFAPI_PROTO_TRACE_DIR: ${WINDSURFAPI_PROTO_TRACE_DIR:-/data/proto-trace} LS_BINARY_PATH: /opt/windsurf/language_server_linux_x64 # REPLICA_ISOLATE=1 puts telemetry under per-container subdirs. # Default off — accounts.json now always lives at the shared # /data/accounts.json regardless of this flag (see src/config.js # sharedDataDir). Set to 1 only if you scale replicas>1 and accept # that runtime-config / model-access / cache / cascade pool stay # replica-local until externalized. See issue #67 / #69. REPLICA_ISOLATE: 0 # 429 死循环缓解(F1/F2/F3)自 2026-07-12 起代码默认已开启,Docker 无需 # 显式设置即生效:degradedServe=on / 裸429冷却=15s / 客户端 Retry-After # 地板=30s。要回退或微调,在 .env 里设 WINDSURFAPI_DEGRADED_SERVE=0 / # WINDSURFAPI_RL_BURST_MS=300000 / WINDSURFAPI_RL_CLIENT_BACKOFF_FLOOR_MS=0 # (见 .env.example 的 "429 缓解" 段),或用面板设置页热调。 # Do not expose ports directly when load balancing expose: - "3003" volumes: - ./.docker-data/data:/data - ./.docker-data/opt/windsurf:/opt/windsurf - ./.docker-data/tmp/windsurf-workspace:/tmp/windsurf-workspace # Optional: mount the docker socket to enable in-dashboard one-click # updates for docker deployments. With this mount the dashboard's # "Check for update" button can pull the latest image AND trigger # `docker compose up -d` to recreate the container — no manual # SSH/exec needed. SECURITY: a process with access to docker.sock # is effectively root on the host (it can spawn privileged # containers, mount any path, etc.). Only enable on machines where # the dashboard is locked down (DASHBOARD_PASSWORD set, not exposed # publicly without a strong API_KEY). Leave commented for the # default secure-by-default deployment. # - /var/run/docker.sock:/var/run/docker.sock # Default to a single replica. Each replica keeps its own in-memory # response cache, cascade reuse pool, runtime-config, and model-access # list — those are NOT yet shared. Multi-replica is opt-in: # `docker compose up -d --scale windsurf-api=3` after you've set up # external coordination for those state files. See issue #69. deploy: replicas: 1 nginx: image: nginx:alpine container_name: windsurf-lb restart: unless-stopped ports: - "${PORT:-3003}:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - windsurf-api