# Deploy Fish Audio S2-Pro (richest paralinguistic open-source TTS) to # irv-ml1. # # Builds the image locally from fishaudio/fish-speech via docker buildx # git URL context. ~10-15 min cold build (CUDA 12.x + torch + flash-attn # + Fish's training/inference deps). First start downloads s2-pro # (~9 GB BF16) into the bind-mounted HF cache. Generous /v1/health # wait deadline accommodates both. # # Usage: # scripts/elway irv-ml1 --playbook playbooks/deploy-fish-s2.yaml # # Idempotent — every step is creates-/when-gated; rerun is safe. vars: compose_dir: /opt/docker/compose/fish-s2 references_dir: /worktank/fish-s2/references checkpoints_dir: /worktank/fish-s2/checkpoints cache_dir: /worktank/fish-s2/hf_cache host_port: "8195" steps: # ── host-side dirs ────────────────────────────────────────────────── - name: Ensure /worktank/fish-s2 root exists (one-time, sudo) shell: mkdir -p /worktank/fish-s2 sudo: true creates: /worktank/fish-s2 - name: Chown /worktank/fish-s2 to lkraven shell: chown -R lkraven:lkraven /worktank/fish-s2 sudo: true when: "[ \"$(stat -c %U /worktank/fish-s2)\" != \"lkraven\" ]" - name: Ensure references dir exists shell: mkdir -p {{ references_dir }} creates: "{{ references_dir }}" - name: Ensure checkpoints dir exists shell: mkdir -p {{ checkpoints_dir }} creates: "{{ checkpoints_dir }}" - name: Ensure HF cache dir exists shell: mkdir -p {{ cache_dir }} creates: "{{ cache_dir }}" - name: Ensure compose dir exists shell: mkdir -p {{ compose_dir }} creates: "{{ compose_dir }}" # ── deploy compose + env ──────────────────────────────────────────── - name: Upload compose.yaml upload: src: stacks/fish-s2/compose.yaml dest: "{{ compose_dir }}/compose.yaml" mode: "0644" - name: Seed .env from template (only if absent) upload: src: stacks/fish-s2/.env.example dest: "{{ compose_dir }}/.env" mode: "0644" when: "[ ! -f {{ compose_dir }}/.env ]" # ── build + bring up ──────────────────────────────────────────────── - name: docker compose build (~10-15 min first time; cached after) shell: | set -o pipefail cd {{ compose_dir }} && docker compose build 2>&1 \ | grep -vE '^#[0-9]+ |^ => |^=> |Collecting|Downloading|Requirement|Using cached|Installing collected|Successfully (installed|built)|━' - name: docker compose up -d shell: cd {{ compose_dir }} && docker compose up -d - name: Wait for /v1/health to respond (allow ~15 min for first model download + warmup) shell: | for i in $(seq 1 180); do curl -sf -o /dev/null --max-time 3 http://localhost:{{ host_port }}/v1/health && exit 0 sleep 5 done exit 1 changed_when: "false" verify: - name: /v1/health returns 200 shell: curl -sf -o /dev/null http://localhost:{{ host_port }}/v1/health changed_when: "false" - name: /v1/audio/voices returns valid JSON shell: | curl -sf http://localhost:{{ host_port }}/v1/audio/voices \ | python3 -c "import json,sys; json.load(sys.stdin)" changed_when: "false" - name: Container is running shell: docker inspect fish-s2 --format '{{.State.Status}}' | grep -q running changed_when: "false"