# Fish Audio S2-Pro — the most expressive open-source TTS as of # 2026-04. 4B params, dual-AR architecture (Slow AR for semantic # codebook + Fast AR for 9 residual codebooks), trained on 10M+ hours # across 80+ languages. ~150 ms streaming TTFB on warm GPU. # # Why this stack alongside the existing TTS: # * Headline feature: 15,000+ paralinguistic / emotion tags via # natural language, e.g. [laugh] [whispers] [super happy] [sigh]. # Chatterbox Turbo only has 9 fixed tags — Fish's vocabulary is # dramatically richer for any emotive use case. # * 91.61% paralinguistic win rate on EmergentTTS-Eval — currently # the leader on that benchmark. # * Voice cloning + native multi-speaker / multi-turn generation. # * MIT-style license (weights, training code, inference engine all # open). # # Image is built locally from upstream's repo via docker buildx # git-context. Upstream ships a compose with `--profile server` for # the API path; we adapt that to our `restart: unless-stopped` # convention + bind-mount layout. # # All tunables live in .env — edit that, not this file. services: fish-s2: image: local/fish-s2:${FISH_S2_TAG} build: context: https://github.com/fishaudio/fish-speech.git#${FISH_S2_SHA} # The REAL production Dockerfile is at docker/Dockerfile (per # upstream's compose.base.yml). The repo also ships a # `dockerfile.dev` at root which is a thin # `FROM ghcr.io/fishaudio/fish-speech:${VERSION}` wrapper meant # for dev iteration on top of a private base image — that path # 403s on anonymous pulls. Build from source via docker/Dockerfile # instead. dockerfile: docker/Dockerfile # Multi-stage Dockerfile — `server` stage installs start_server.sh # and exposes the API. The default last stage is `webui` (gradio # only), which crashloops silently because start_server.sh isn't # there. Target=server is what upstream's compose.yml does. target: server args: # Build args mirror upstream compose.base.yml defaults. # CUDA_VER 12.9 + UV_EXTRA cu129 = the CUDA 12.9 PyTorch wheels. # irv-ml1's driver (595.58.03 / CUDA 13.2 capable) is # backward-compatible with 12.9-built images. BACKEND: cuda CUDA_VER: "12.9.0" UV_EXTRA: cu129 UV_VERSION: "0.8.15" container_name: fish-s2 restart: unless-stopped runtime: nvidia ports: - "${FISH_S2_BIND:-0.0.0.0}:${FISH_S2_PORT}:8080" environment: - NVIDIA_VISIBLE_DEVICES=${FISH_S2_GPU_DEVICES:-1} - BACKEND=cuda # COMPILE=1 enables torch.compile — upstream claims ~10× speedup # on the autoregressive forward, at the cost of ~60 s warmup the # first time each input shape is seen. Worth the speedup; turn # off via .env if you hit a torch.compile bug on a future model # checkpoint. - COMPILE=${FISH_S2_COMPILE:-1} - API_PORT=8080 # Model selection. Override Fish's Dockerfile defaults so we can # swap variants via .env without rebuilding. Both checkpoints are # pre-pulled by the deploy playbook into bind-mounted checkpoints/. # s2-pro — 4B class, ~17 GB VRAM, slow but highest quality # s1-mini — lighter, ~3-5× smaller model.pth, much faster - LLAMA_CHECKPOINT_PATH=checkpoints/${FISH_S2_MODEL:-s2-pro} - DECODER_CHECKPOINT_PATH=checkpoints/${FISH_S2_MODEL:-s2-pro}/codec.pth # Hugging Face cache for model weights — first start pulls # fishaudio/s2-pro (~9 GB BF16) into this dir. - HF_HOME=/app/hf_cache volumes: # Model checkpoints (auto-downloaded on first run, then cached). - ${FISH_S2_CHECKPOINT_DIR}:/app/checkpoints # Reference audio for voice cloning — drop ~5–15 s WAV clips here. - ${FISH_S2_REFERENCE_DIR}:/app/references # Persistent HF cache so model re-pull only happens on first deploy. - ${FISH_S2_CACHE_DIR}:/app/hf_cache # Pass --half to start_server.sh → enables fp16 inference on the # LLM half. Speeds up the autoregressive loop (the dominant cost # in TTFB). Also enables the streaming path's faster total time. # build_compile_args() echoes unknown args back to the exec line. command: ["--half"] healthcheck: # Fish ships /v1/health on the API server. python urllib instead # of curl because the upstream image is python-based and may not # carry curl. 127.0.0.1 explicit to dodge the IPv6-first # localhost trap we hit on chatterbox + news-digest. test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8080/v1/health', timeout=5).status==200 else 1)\""] interval: 30s timeout: 10s retries: 3 # First boot: torch.compile warmup + first-pull HF download + # checkpoint load. Generous deadline. start_period: 900s labels: - homepage.group=AI Systems - homepage.name=Fish Audio S2-Pro - homepage.icon=mdi-fish - homepage.description=Most expressive open-source TTS — 15k+ paralinguistic tags, voice cloning, 80+ languages (irv-ml1) - homepage.href=http://10.100.79.3:${FISH_S2_PORT}