# PROD stack: Speaches server + this UI + Caddy reverse proxy. # # Single origin via Caddy so the phone browser only ever talks to one host # (no CORS, and the realtime WebSocket works through the proxy): # /v1/* -> speaches:8000 (REST + WS /v1/realtime) # everything else -> ui:3000 (TanStack Start node server) # # Pick CPU or GPU by layering an override: # docker compose -f deploy/compose.yaml -f deploy/compose.cpu.yaml up -d # docker compose -f deploy/compose.yaml -f deploy/compose.cuda.yaml up -d # # `.env` in the project root supplies SPEACHES_URL etc. for the ui service. # Speaches model cache is persisted in the hf-hub-cache named volume. services: speaches: # Image/flags chosen by the override (compose.cpu.yaml / compose.cuda.yaml). # Sensible CPU defaults live here so the base file alone is runnable. image: ghcr.io/speaches-ai/speaches:latest-cpu restart: unless-stopped ports: - "8000:8000" # direct access for CLI / curl smoke tests volumes: - hf-hub-cache:/home/ubuntu/.cache/huggingface/hub healthcheck: test: ["CMD", "curl", "--fail", "http://0.0.0.0:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 30s ui: build: context: .. dockerfile: deploy/ui.Dockerfile restart: unless-stopped depends_on: speaches: condition: service_healthy environment: # Server-side: the UI proxies to Speaches over the compose network. - SPEACHES_URL=http://speaches:8000 - NODE_ENV=production - PORT=3000 - HOST=0.0.0.0 expose: - "3000" caddy: image: caddy:2-alpine restart: unless-stopped depends_on: - ui - speaches ports: - "8080:80" # phone entrypoint. Front with Tailscale/Tunnel for TLS. volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config volumes: hf-hub-cache: caddy-data: caddy-config: