deploy: Docker Compose stack with Caddy + Cloudflare TLS

Full production stack via docker compose:
- Caddy reverse proxy with Cloudflare DNS-01 TLS certs
- warzone-server (featherChat API + web UI)
- wzp-relay (QUIC audio SFU)
- wzp-web (browser WS ↔ QUIC bridge)

Architecture:
  Internet → Caddy (443/TLS) → voip.manko.yoga
    /*       → warzone-server:7700
    /audio/* → wzp-web:8080

Files:
- docker-compose.yml: main stack (4 services)
- docker-compose.ipv6.yml: IPv6 overlay
- Caddyfile: Cloudflare DNS challenge + reverse proxy
- Dockerfile.server: featherChat multi-stage build
- Dockerfile.wzp: wzp-relay + wzp-web multi-stage build
- .env.example: DNS records for dev/staging/prod
- test-stack.sh: smoke test (8 checks)
- .dockerignore: excludes target/, .git/, etc.

Deployment targets:
  dev:  172.16.81.135
  ipv6: 2a0d:3344:692c:2500:14f2:5885:d73c:b0a1
  prod: 63.250.54.239 / 2602:ff16:9:0:1:3d9:0:1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-03-30 10:00:47 +04:00
parent c2be68ca20
commit f33ac1cad8
8 changed files with 278 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
# featherChat + WZP full stack
# Usage:
# echo "YOUR_CF_API_TOKEN" > cf_api_token.txt
# docker compose up -d
#
# DNS: voip.manko.yoga → your IP
# Test: https://voip.manko.yoga
services:
# ─── Caddy reverse proxy (TLS termination) ───
caddy:
image: ghcr.io/caddy-dns/cloudflare:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3 (QUIC)
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
environment:
CF_API_TOKEN: /run/secrets/cf_api_token
secrets:
- cf_api_token
# Caddy reads CF_API_TOKEN; the caddy-cloudflare image supports file-based secrets
# via the {$CF_API_TOKEN} placeholder in Caddyfile.
# We mount the secret and set env to its content at runtime.
entrypoint: ["/bin/sh", "-c", "export CF_API_TOKEN=$(cat /run/secrets/cf_api_token) && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"]
depends_on:
- warzone-server
- wzp-web
networks:
- frontend
- backend
# ─── featherChat server ───
warzone-server:
build:
context: ../../..
dockerfile: warzone/deploy/docker/Dockerfile.server
restart: unless-stopped
environment:
# Browser connects to audio via Caddy: wss://voip.manko.yoga/audio/ws/ROOM
WZP_RELAY_ADDR: "voip.manko.yoga/audio"
RUST_LOG: "info"
volumes:
- server_data:/data
command: ["--bind", "0.0.0.0:7700", "--enable-bots"]
networks:
- backend
# ─── WZP QUIC relay (audio SFU) ───
wzp-relay:
build:
context: ../../..
dockerfile: warzone/deploy/docker/Dockerfile.wzp
restart: unless-stopped
entrypoint: ["wzp-relay"]
command:
- "--listen"
- "0.0.0.0:4433"
- "--auth-url"
- "http://warzone-server:7700/v1/auth/validate"
networks:
- backend
# ─── WZP web bridge (browser WS ↔ QUIC relay) ───
wzp-web:
build:
context: ../../..
dockerfile: warzone/deploy/docker/Dockerfile.wzp
restart: unless-stopped
entrypoint: ["wzp-web"]
command:
- "--port"
- "8080"
- "--relay"
- "wzp-relay:4433"
- "--auth-url"
- "http://warzone-server:7700/v1/auth/validate"
depends_on:
- wzp-relay
- warzone-server
networks:
- backend
secrets:
cf_api_token:
file: ./cf_api_token.txt
volumes:
caddy_data:
caddy_config:
server_data:
networks:
frontend:
backend: