Files
ds2api/DEPLOY.en.md

14 KiB

DS2API Deployment Guide

Language: 中文 | English

This guide covers all deployment methods for the current Go-based codebase.


Table of Contents


0. Prerequisites

Dependency Minimum Version Notes
Go 1.24+ Build backend
Node.js 20+ Only needed to build WebUI locally
npm Bundled with Node.js Install WebUI dependencies

Config source (choose one):

  • File: config.json (recommended for local/Docker)
  • Environment variable: DS2API_CONFIG_JSON (recommended for Vercel; supports raw JSON or Base64)

Unified recommendation (best practice):

cp config.example.json config.json
# Edit config.json

Use config.json as the single source of truth:

  • Local run: read config.json directly
  • Docker / Vercel: generate DS2API_CONFIG_JSON (Base64) from config.json and inject it

1. Local Run

1.1 Basic Steps

# Clone
git clone https://github.com/CJackHwang/ds2api.git
cd ds2api

# Copy and edit config
cp config.example.json config.json
# Open config.json and fill in:
#   - keys: your API access keys
#   - accounts: DeepSeek accounts (email or mobile + password)

# Start
go run ./cmd/ds2api

Default address: http://0.0.0.0:5001 (override with PORT).

1.2 WebUI Build

On first local startup, if static/admin/ is missing, DS2API will automatically attempt to build the WebUI (requires Node.js/npm).

Manual build:

./scripts/build-webui.sh

Or step by step:

cd webui
npm install
npm run build
# Output goes to static/admin/

Control auto-build via environment variable:

# Disable auto-build
DS2API_AUTO_BUILD_WEBUI=false go run ./cmd/ds2api

# Force enable auto-build
DS2API_AUTO_BUILD_WEBUI=true go run ./cmd/ds2api

1.3 Compile to Binary

go build -o ds2api ./cmd/ds2api
./ds2api

2. Docker Deployment

2.1 Basic Steps

# Copy env template
cp .env.example .env

# Generate single-line Base64 from config.json
DS2API_CONFIG_JSON="$(base64 < config.json | tr -d '\n')"

# Edit .env and set:
#   DS2API_ADMIN_KEY=your-admin-key
#   DS2API_CONFIG_JSON=${DS2API_CONFIG_JSON}

# Start
docker-compose up -d

# View logs
docker-compose logs -f

2.2 Update

docker-compose up -d --build

2.3 Docker Architecture

The Dockerfile uses a three-stage build:

  1. WebUI build stage: node:20 image, runs npm ci && npm run build
  2. Go build stage: golang:1.24 image, compiles the binary
  3. Runtime stage: debian:bookworm-slim minimal image

Container entry command: /usr/local/bin/ds2api, default exposed port: 5001.

2.4 Development Mode

docker-compose -f docker-compose.dev.yml up

Development features:

  • Source code mounted (live changes)
  • LOG_LEVEL=DEBUG
  • No auto-restart

2.5 Health Check

Docker Compose includes a built-in health check:

healthcheck:
  test: ["CMD", "wget", "-qO-", "http://localhost:${PORT:-5001}/healthz"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 10s

2.6 Docker Troubleshooting

If container logs look normal but the admin panel is unreachable, check these first:

  1. Port alignment: when PORT is not 5001, use the same port in your URL (for example http://localhost:8080/admin).
  2. WebUI assets in dev compose: docker-compose.dev.yml runs go run in a dev image and does not auto-install Node.js inside the container; if static/admin is missing in your repo, /admin will return 404. Build once on host: ./scripts/build-webui.sh.

3. Vercel Deployment

3.1 Steps

  1. Fork the repo to your GitHub account

  2. Import the project on Vercel

  3. Set environment variables (minimum required: one variable):

    Variable Description
    DS2API_ADMIN_KEY Admin key (required)
    DS2API_CONFIG_JSON Config content, raw JSON or Base64 (optional, recommended)
  4. Deploy

If you prefer faster one-click bootstrap, you can leave DS2API_CONFIG_JSON empty first, then open /admin after deployment, import config, and sync it back to Vercel env vars from the "Vercel Sync" page.

Recommended: in repo root, copy the template first and fill your real accounts:

cp config.example.json config.json
# Edit config.json

Do not hand-edit large JSON directly in Vercel. Generate Base64 locally and paste it:

# Run in repo root
DS2API_CONFIG_JSON="$(base64 < config.json | tr -d '\n')"
echo "$DS2API_CONFIG_JSON"

If you choose to preconfigure before first deploy, set these vars in Vercel Project Settings -> Environment Variables:

DS2API_ADMIN_KEY=replace-with-a-strong-secret
DS2API_CONFIG_JSON=<the single-line Base64 output above>

Optional but recommended (for WebUI one-click Vercel sync):

VERCEL_TOKEN=your-vercel-token
VERCEL_PROJECT_ID=prj_xxxxxxxxxxxx
VERCEL_TEAM_ID=team_xxxxxxxxxxxx   # optional for personal accounts

3.2 Optional Environment Variables

Variable Description Default
DS2API_ACCOUNT_MAX_INFLIGHT Per-account inflight limit 2
DS2API_ACCOUNT_CONCURRENCY Alias (legacy compat)
DS2API_ACCOUNT_MAX_QUEUE Waiting queue limit recommended_concurrency
DS2API_ACCOUNT_QUEUE_SIZE Alias (legacy compat)
DS2API_VERCEL_INTERNAL_SECRET Hybrid streaming internal auth Falls back to DS2API_ADMIN_KEY
DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS Stream lease TTL 900
VERCEL_TOKEN Vercel sync token
VERCEL_PROJECT_ID Vercel project ID
VERCEL_TEAM_ID Vercel team ID
DS2API_VERCEL_PROTECTION_BYPASS Deployment protection bypass for internal Node→Go calls

3.3 Vercel Architecture

Request ──────┐
              │
              ▼
         vercel.json routing
              │
        ┌─────┴─────┐
        │           │
        ▼           ▼
  api/index.go   api/chat-stream.js
  (Go Runtime)   (Node Runtime)
  • Go entry: api/index.go (Serverless Go)
  • Stream entry: api/chat-stream.js (Node Runtime for real-time SSE)
  • Routing: vercel.json
  • Build command: npm ci --prefix webui && npm run build --prefix webui (automatic)

Streaming Pipeline

Vercel Go Runtime applies platform-level response buffering, so this project uses a hybrid "Go prepare + Node stream" path on Vercel:

  1. api/chat-stream.js receives /v1/chat/completions request
  2. Node calls Go internal prepare endpoint (?__stream_prepare=1) for session ID, PoW, token
  3. Go prepare creates a stream lease, locking the account
  4. Node connects directly to DeepSeek upstream, relays SSE in real-time to client (including OpenAI chunk framing and tools anti-leak sieve)
  5. After stream ends, Node calls Go release endpoint (?__stream_release=1) to free the account

This adaptation is Vercel-only; local and Docker remain pure Go.

Non-Stream Fallback and Tool Call Handling

  • api/chat-stream.js falls back to Go entry (?__go=1) for non-stream requests only
  • Streaming requests (including requests with tools) stay on the Node path and use Go-aligned tool-call anti-leak handling
  • WebUI non-stream test calls ?__go=1 directly to avoid Node hop timeout on long requests

Function Duration

vercel.json sets maxDuration: 300 for both api/chat-stream.js and api/index.go (subject to your Vercel plan limits).

3.4 Vercel Troubleshooting

Go Build Failure

Error: Command failed: go build -ldflags -s -w -o .../bootstrap ...

Cause: Invalid Go build flag settings in Vercel (-ldflags not passed as a single argument).

Fix:

  1. Open Vercel Project Settings → Build and Development Settings
  2. Clear custom Go Build Flags / Build Command (recommended)
  3. If ldflags must be used, set -ldflags="-s -w" (ensure it's one argument)
  4. Verify go.mod uses a supported version (currently go 1.24)
  5. Redeploy (recommended: clear cache)

Internal Package Import Error

use of internal package ds2api/internal/server not allowed

Cause: Vercel Go entrypoint directly imports internal/....

Fix: This repo uses a public bridge package: api/index.gods2api/appinternal/server.

Output Directory Error

No Output Directory named "public" found after the Build completed.

Fix: This repo uses static as output directory ("outputDirectory": "static" in vercel.json). If you manually changed Output Directory in Project Settings, set it to static or clear it.

Deployment Protection Blocking

If API responses return Vercel HTML Authentication Required:

  • Option A: Disable Deployment Protection for that environment (recommended for public APIs)
  • Option B: Add x-vercel-protection-bypass header to requests
  • Option C: Set VERCEL_AUTOMATION_BYPASS_SECRET (or DS2API_VERCEL_PROTECTION_BYPASS) for internal Node→Go calls

3.5 Build Artifacts Not Committed

  • static/admin directory is not in Git
  • Vercel / Docker automatically generate WebUI assets during build

4. Download Release Binaries

Built-in GitHub Actions workflow: .github/workflows/release-artifacts.yml

  • Trigger: only on Release published (no build on normal push)
  • Outputs: multi-platform binary archives + sha256sums.txt
Platform Architecture Format
Linux amd64, arm64 .tar.gz
macOS amd64, arm64 .tar.gz
Windows amd64 .zip

Each archive includes:

  • ds2api executable (ds2api.exe on Windows)
  • static/admin/ (built WebUI assets)
  • sha3_wasm_bg.7b9ca65ddd.wasm
  • config.example.json, .env.example
  • README.MD, README.en.md, LICENSE

Usage

# 1. Download the archive for your platform
# 2. Extract
tar -xzf ds2api_v1.7.0_linux_amd64.tar.gz
cd ds2api_v1.7.0_linux_amd64

# 3. Configure
cp config.example.json config.json
# Edit config.json

# 4. Start
./ds2api

Maintainer Release Flow

  1. Create and publish a GitHub Release (with tag, e.g. v1.7.0)
  2. Wait for the Release Artifacts workflow to complete
  3. Download the matching archive from Release Assets

5. Reverse Proxy (Nginx)

When deploying behind Nginx, you must disable buffering for SSE streaming to work:

location / {
    proxy_pass http://127.0.0.1:5001;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_buffering off;
    proxy_cache off;
    chunked_transfer_encoding on;
    tcp_nodelay on;
}

For HTTPS, add SSL at the Nginx layer:

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://127.0.0.1:5001;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding on;
        tcp_nodelay on;
    }
}

6. Linux systemd Service

6.1 Installation

# Copy compiled binary and related files to target directory
sudo mkdir -p /opt/ds2api
sudo cp ds2api config.json sha3_wasm_bg.7b9ca65ddd.wasm /opt/ds2api/
sudo cp -r static/admin /opt/ds2api/static/admin

6.2 Create systemd Service File

# /etc/systemd/system/ds2api.service

[Unit]
Description=DS2API (Go)
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/ds2api
Environment=PORT=5001
Environment=DS2API_CONFIG_PATH=/opt/ds2api/config.json
Environment=DS2API_ADMIN_KEY=your-admin-key-here
ExecStart=/opt/ds2api/ds2api
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

6.3 Common Commands

# Reload service config
sudo systemctl daemon-reload

# Enable on boot
sudo systemctl enable ds2api

# Start
sudo systemctl start ds2api

# Check status
sudo systemctl status ds2api

# View logs
sudo journalctl -u ds2api -f

# Restart
sudo systemctl restart ds2api

# Stop
sudo systemctl stop ds2api

7. Post-Deploy Checks

After deployment (any method), verify in order:

# 1. Liveness probe
curl -s http://127.0.0.1:5001/healthz
# Expected: {"status":"ok"}

# 2. Readiness probe
curl -s http://127.0.0.1:5001/readyz
# Expected: {"status":"ready"}

# 3. Model list
curl -s http://127.0.0.1:5001/v1/models
# Expected: {"object":"list","data":[...]}

# 4. Admin panel (if WebUI is built)
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5001/admin
# Expected: 200

# 5. Test API call
curl http://127.0.0.1:5001/v1/chat/completions \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"hello"}]}'

8. Pre-Release Local Regression

Run the full live testsuite before release (real account tests):

./scripts/testsuite/run-live.sh

With custom flags:

go run ./cmd/ds2api-tests \
  --config config.json \
  --admin-key admin \
  --out artifacts/testsuite \
  --timeout 120 \
  --retries 2

The testsuite automatically performs:

  • Preflight checks (syntax/build/unit tests)
  • Isolated config copy startup (no mutation to your original config.json)
  • Live scenario verification (OpenAI/Claude/Admin/concurrency/toolcall/streaming)
  • Full request/response artifact logging for debugging

For detailed testsuite documentation, see TESTING.md.