# Deploy IndexTTS-2 (https://github.com/index-tts/index-tts) to irv-ml1 # behind our own FastAPI wrapper (stacks/index-tts/app.py). # # Builds the image locally from the Dockerfile in stacks/index-tts/ # (which clones the upstream IndexTTS-2 repo at a pinned SHA inside # the build), stages app.py + entrypoint.sh + compose + .env under # /opt/docker/compose/index-tts/, brings it up, waits for /healthz, # and verifies the API surface. # # First run is slow: ~5-10 min for the docker build (CUDA torch + the # IndexTTS pinned-deps tail) plus ~5-7 GB model download from HF on # first container start (entrypoint.sh handles that). The healthz wait # below allows up to 15 min total. # # Usage: # scripts/elway irv-ml1 --playbook playbooks/deploy-index-tts.yaml # # Idempotent — every step is creates-/when-gated; rerun is safe. vars: compose_dir: /opt/docker/compose/index-tts cache_dir: /worktank/index-tts/cache voices_dir: /worktank/index-tts/voices emotions_dir: /worktank/index-tts/emotions host_port: "8192" steps: # ── host-side dirs ────────────────────────────────────────────────── - name: Ensure /worktank/index-tts root exists (one-time, sudo) shell: mkdir -p /worktank/index-tts sudo: true creates: /worktank/index-tts - name: Chown /worktank/index-tts to lkraven shell: chown lkraven:lkraven /worktank/index-tts sudo: true when: '[ "$(stat -c %U /worktank/index-tts)" != lkraven ]' - name: Ensure cache dir exists shell: mkdir -p {{ cache_dir }} creates: "{{ cache_dir }}" - name: Ensure voices dir exists shell: mkdir -p {{ voices_dir }} creates: "{{ voices_dir }}" - name: Ensure emotions dir exists shell: mkdir -p {{ emotions_dir }} creates: "{{ emotions_dir }}" - name: Ensure compose dir exists shell: mkdir -p {{ compose_dir }} creates: "{{ compose_dir }}" # ── deploy build context (compose, env, dockerfile, app, entrypoint) ── - name: Upload compose.yaml upload: src: stacks/index-tts/compose.yaml dest: "{{ compose_dir }}/compose.yaml" mode: "0644" - name: Upload Dockerfile upload: src: stacks/index-tts/Dockerfile dest: "{{ compose_dir }}/Dockerfile" mode: "0644" - name: Upload app.py upload: src: stacks/index-tts/app.py dest: "{{ compose_dir }}/app.py" mode: "0644" - name: Upload entrypoint.sh upload: src: stacks/index-tts/entrypoint.sh dest: "{{ compose_dir }}/entrypoint.sh" mode: "0755" - name: Seed .env from template (only if absent) upload: src: stacks/index-tts/.env.example dest: "{{ compose_dir }}/.env" mode: "0644" when: "[ ! -f {{ compose_dir }}/.env ]" # ── build + bring up ──────────────────────────────────────────────── - name: docker compose build (~5-10 min first time; cached after) # --progress=plain stops carriage-return TUI from littering the log. # The grep drops pip's per-package Downloading/Collecting/Requirement # noise (~25% of the log on first build) while keeping buildkit step # transitions and DONE/CACHED/ERROR markers. set -o pipefail so a # build failure isn't swallowed by the grep return code. shell: | set -o pipefail cd {{ compose_dir }} && docker compose build --progress=plain 2>&1 \ | grep -vE '^#[0-9]+ [0-9.]+ (Downloading|Collecting|Requirement|Using cached|Installing collected|Successfully (installed|built)|Saved /|━|Resolved|Prepared|Built)' - name: docker compose up -d shell: cd {{ compose_dir }} && docker compose up -d - name: Wait for /healthz to respond (allow ~15 min for model download) shell: | for i in $(seq 1 180); do curl -sf -o /dev/null --max-time 3 http://localhost:{{ host_port }}/healthz && exit 0 sleep 5 done exit 1 changed_when: "false" verify: - name: /healthz returns 200 shell: curl -sf -o /dev/null http://localhost:{{ host_port }}/healthz changed_when: "false" - name: /v1/voices returns a JSON object with 'voices' and 'emotions' keys shell: curl -sf http://localhost:{{ host_port }}/v1/voices | grep -q '"voices"' changed_when: "false" - name: Container is running shell: docker inspect index-tts --format '{{.State.Status}}' | grep -q running changed_when: "false"