From c53db4b251ac8e60365a43f28ff7c6dc95dafbe5 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Fri, 20 Feb 2026 20:47:22 -0700 Subject: [PATCH] Add Docker deployment with Caddy HTTPS reverse proxy CPU-only torch image (~180MB vs 873MB CUDA), PipeWire socket passthrough for audio playback, SNAC HuggingFace cache volume. Served at voice.l.supported.systems via caddy-docker-proxy. --- Dockerfile | 41 +++++++++++++++++++++++++++++++++++++++++ Makefile | 28 ++++++++++++++++++++++++++++ docker-compose.yml | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1052701 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim + +# PipeWire client tools — pw-play connects to the host's PipeWire socket +RUN apt-get update && apt-get install -y --no-install-recommends \ + pipewire-bin \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +ENV UV_COMPILE_BYTECODE=1 + +# Create venv +RUN uv venv + +# Install CPU-only torch first (saves ~3GB vs CUDA — SNAC runs on CPU anyway) +RUN --mount=type=cache,target=/root/.cache/uv \ + uv pip install torch --index-url https://download.pytorch.org/whl/cpu + +# Install project dependencies (torch already satisfied, won't redownload) +COPY pyproject.toml ./ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv pip install --no-deps -r pyproject.toml 2>/dev/null || true + +# Copy source and install project +COPY src/ src/ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv pip install . + +# Non-root user matching host uid for PipeWire socket access +RUN useradd -u 1000 -m tts \ + && mkdir -p /home/tts/.cache/huggingface \ + && chown -R tts:tts /home/tts/.cache +USER tts + +ENV PATH="/app/.venv/bin:$PATH" +EXPOSE 8371 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=15s \ + CMD python -c "import socket; s=socket.create_connection(('127.0.0.1',8371),2); s.close()" || exit 1 + +CMD ["tts-mcp"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9d2cb52 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +include .env +export + +.PHONY: build up down logs restart status + +build: + docker compose build + +up: build + docker compose up -d + @sleep 2 + docker compose logs --tail 20 + +down: + docker compose down + +logs: + docker compose logs -f + +restart: + docker compose restart + @sleep 2 + docker compose logs --tail 20 + +status: + @docker compose ps + @echo "---" + @curl -s http://localhost:8371/mcp 2>/dev/null | head -5 || echo "Server not responding" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3d0e0b5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +services: + tts-mcp: + build: . + container_name: tts-mcp + restart: unless-stopped + env_file: .env + environment: + # Override for Docker networking (container DNS instead of IPs) + TTS_PIPER_HOST: piper-tts + TTS_OLLAMA_URL: http://host.docker.internal:11434 + # PipeWire client config + XDG_RUNTIME_DIR: /run/user/1000 + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + # Kokoro ONNX models (read-only) + - ./models:/app/models:ro + # HuggingFace cache for SNAC model download + - hf-cache:/home/tts/.cache/huggingface + # PipeWire socket for audio playback through host speakers + - /run/user/1000/pipewire-0:/run/user/1000/pipewire-0 + networks: + - caddy + - dootie-internal + labels: + caddy: voice.l.supported.systems + caddy.reverse_proxy: "{{upstreams 8371}}" + +volumes: + hf-cache: + +networks: + caddy: + external: true + dootie-internal: + external: true