services: # The web process talks to this narrowly configured proxy over the Compose # network; the host Docker socket is never mounted into the web container. docker-socket-proxy: image: tecnativa/docker-socket-proxy:0.3.0 container_name: tts-voice-creator-docker-proxy restart: unless-stopped # NOT read_only: the entrypoint sed's its own haproxy.cfg.template into # haproxy.cfg at startup, in the same directory the template ships in -- # read_only crash-looped it ("Read-only file system"), and a tmpfs mount # over that directory only trades one failure for another by hiding the # template it needs to read (confirmed live: "No such file or # directory"). The real hardening here is cap_drop: ALL, # no-new-privileges, and the Docker socket itself being mounted :ro. cap_drop: - ALL security_opt: - no-new-privileges:true environment: CONTAINERS: "1" POST: "1" IMAGES: "0" NETWORKS: "0" VOLUMES: "0" SERVICES: "0" TASKS: "0" SWARM: "0" SYSTEM: "0" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro networks: - dgx_net tts-voice-creator-clone-and-design-2: build: context: . dockerfile: Dockerfile image: ghcr.io/martin-b78/tts-voice-creator-clone-and-design-2:${APP_TAG:-latest} container_name: tts-voice-creator-clone-and-design-2 restart: unless-stopped # The application writes only to the explicitly mounted data paths below. # Keep the image filesystem immutable even if an endpoint is compromised. read_only: true cap_drop: - ALL security_opt: - no-new-privileges:true tmpfs: - /tmp:size=1g,mode=1777 # Keep a runaway OCR/import/generation request from consuming all host RAM. # Override these per machine when large PDF jobs need more headroom: # APP_MEM_LIMIT=12g APP_MEM_RESERVATION=2g docker compose up -d mem_limit: "${APP_MEM_LIMIT:-8g}" mem_reservation: "${APP_MEM_RESERVATION:-2g}" ports: - "7890:7890" depends_on: - docker-socket-proxy volumes: - ${VOICE_HOST_DIR:-/home/sparky/Media/_Sounds/TTS_Voices}:/voices:rw - ./config:/home/app/.config/tts-voice-creator:rw # settings, presets, routes - ./logs:/logs:rw # application logs - /home/sparky/Docker/tts-dgx-spark-faster-qwen3-tts/config:/tts-config:rw - ./static:/app/static:ro - ./server.py:/app/server.py:ro - ./core:/app/core:ro - ./routes:/app/routes:ro - ./workflows:/app/workflows:ro - ./VERSION:/app/VERSION:ro - ./CHANGELOG.md:/app/CHANGELOG.md:ro environment: - PYTHONUNBUFFERED=1 - VOICES_DIR=/voices - OUTPUT_DIR=/voices/active_voices # Containers that load WAV files and need a restart after voice changes - TTS_CONTAINER_NAME=tts-faster-qwen3-tts-voiceclone - TTS_CONTAINER_NAMES=tts-faster-qwen3-tts-voiceclone,tts-faster-qwen3-tts-streaming - VOICE_DESIGN_MODEL=Qwen3-TTS-12Hz-1.7B-VoiceDesign - DOCKER_HOST=tcp://docker-socket-proxy:2375 # Word-level forced aligner sidecar; empty disables word timing (sentence timing keeps working) - ALIGNER_URL=${ALIGNER_URL-http://tts-aligner:8000} extra_hosts: - "host.docker.internal:host-gateway" networks: - dgx_net # Word-level forced aligner (torchaudio MMS_FA). Separate image so the app stays torch-free. Reachable only on # the Compose network (no published port); the app calls http://tts-aligner:8000. Weights (~1.2 GB) are # downloaded once into the named volume; after that the service needs no outbound access. tts-aligner: build: context: ./aligner image: tts-aligner:local container_name: tts-aligner restart: unless-stopped read_only: true cap_drop: - ALL security_opt: - no-new-privileges:true tmpfs: - /tmp:size=256m,mode=1777 mem_limit: "${ALIGNER_MEM_LIMIT:-3g}" cpus: "${ALIGNER_CPUS:-4}" environment: - ALIGNER_DEVICE=cpu - ALIGNER_THREADS=4 expose: - "8000" volumes: - aligner-models:/models:rw healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"] interval: 30s timeout: 5s retries: 3 start_period: 60s networks: - dgx_net # Optional GPU variant: `docker compose --profile gpu up -d --build tts-aligner-gpu` (stop tts-aligner first; # both answer to the name tts-aligner). Off by default because the DGX GPU is often full. tts-aligner-gpu: profiles: ["gpu"] build: context: ./aligner args: TORCH_INDEX: https://download.pytorch.org/whl/cu130 image: tts-aligner:gpu container_name: tts-aligner-gpu restart: unless-stopped read_only: true cap_drop: - ALL security_opt: - no-new-privileges:true tmpfs: - /tmp:size=256m,mode=1777 mem_limit: "${ALIGNER_MEM_LIMIT:-3g}" environment: - ALIGNER_DEVICE=cuda expose: - "8000" volumes: - aligner-models:/models:rw deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"] interval: 30s timeout: 5s retries: 3 start_period: 60s networks: dgx_net: aliases: - tts-aligner networks: dgx_net: external: true volumes: aligner-models: