Skip to main content
Engineering

Speaches and Kokoro: Open-Source TTS Showdown

by J4SGON

ElevenLabs charges $0.30 per 1,000 characters. OpenAI's TTS API charges $0.015 per 1,000 characters. We run both TTS and STT locally on an NVIDIA GB10 GPU with zero per-request cost. Here's what we measured, how the tools compare, and how to build the same pipeline.

Why Self-Host Voice

Our YouTube content pipeline generates narration for every video. At 2-3 videos per week with 1,500-word scripts, that's roughly 9,000 characters per video — €2.70/month on ElevenLabs, €0.14/month on OpenAI. Not expensive. But the point isn't cost. The point is:

  1. Latency. Cloud TTS adds 200-500ms round-trip per request. Local TTS generates in under 350ms.
  2. Sovereignty. Voice scripts contain proprietary content. They shouldn't transit third-party APIs.
  3. Flexibility. We can swap models, fine-tune voices, and run custom pipelines without API rate limits.
  4. Cost at scale. When you run 50+ agent calls/day with voice output, cloud pricing compounds fast.

The Stack

Three services, all OpenAI API-compatible, all running in Docker on the same machine:

┌──────────────────────────────────────────────┐
│           NVIDIA GB10 (121.7 GiB RAM)         │
│                                               │
│  ┌─────────────┐  ┌─────────────┐            │
│  │ Kokoro TTS   │  │ Speaches    │            │
│  │ :8880        │  │ STT :8001   │            │
│  │ 248 MiB RAM  │  │ 1.97 GiB    │            │
│  │ GPU-accel    │  │ GPU-accel   │            │
│  └─────────────┘  └─────────────┘            │
│                                               │
│  ┌─────────────┐                            │
│  │ openedai-    │  ← Piper fallback TTS      │
│  │ speech :8002 │    22 MiB RAM (CPU only)   │
│  └─────────────┘                            │
└──────────────────────────────────────────────┘

| Service | Image | Role | RAM | GPU | |---------|-------|------|-----|-----| | Kokoro TTS | ghcr.io/remsky/kokoro-fastapi-gpu | Primary TTS | 248 MiB | Yes | | Speaches | ghcr.io/speaches-ai/speaches:latest-cuda | STT (Whisper) | 1.97 GiB | Yes | | openedai-speech | ghcr.io/matatonic/openedai-speech | Fallback TTS (Piper) | 22 MiB | No |

All three expose OpenAI-compatible endpoints (/v1/audio/speech, /v1/audio/transcriptions). That means any code written for the OpenAI API works against them with a base URL change.

Kokoro TTS — The Primary

Kokoro is a lightweight, high-quality TTS model with an OpenAI-compatible API wrapper. It runs on GPU and generates audio in under 400ms for short clips.

Benchmark

We generated a 19-word test sentence and measured end-to-end API latency:

Input: "Speaches and Kokoro are open source voice models running on local hardware."
Voice: af_heart (warm, natural)

Result: 79,916 bytes MP3, 4.99s audio duration
API latency: 1.11s (including network + encoding)
Real-time factor: 4.49x (generates 4.49x faster than playback)

Voices

Kokoro voice IDs follow {a/b}{f/m}_{name} — a = American, b = British, f = female, m = male. Our default is af_heart (warm, natural female).

curl -X POST http://localhost:8880/v1/audio/speech \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kokoro",
    "input": "Self-hosted AI is not about being anti-cloud. It is about being pro-ownership.",
    "voice": "af_heart"
  }' \
  -o narration.mp3

The gpt-4o-mini-tts model alias exists for drop-in OpenAI compatibility.

Speaches STT — The Transcriber

Speaches runs faster-whisper models for speech-to-text. It supports 99 languages and two model sizes.

Benchmark

We transcribed the Kokoro-generated audio (4.99s clip) back to text:

Model: Systran/faster-whisper-small
Input: kokoro_test.mp3 (79,916 bytes, 4.99s)

Result: "Speeches and Kokoro are open source voice models running on local hardware."
API latency: 10.96s
Accuracy: 100% (1 word difference: "Speaches" → "Speeches" — expected, both are valid spellings)

The 10.96s latency on a 4.99s clip means it's slower than real-time for short clips. This is fine for our use case (transcription is a post-processing step, not interactive). For longer audio, the throughput improves significantly since model loading is amortized.

curl -X POST http://localhost:8001/v1/audio/transcriptions \
  -F "model=Systran/faster-whisper-small" \
  -F "file=@narration.mp3"

Two models available: faster-whisper-small (faster) and faster-whisper-medium (more accurate). Both support 99 languages.

openedai-speech (Piper) — The Fallback

openedai-speech wraps Piper TTS models behind an OpenAI-compatible API. It's CPU-only and uses 22 MiB of RAM — a featherweight fallback when the GPU is busy or Kokoro is down.

Benchmark

Same 19-word test sentence across all 6 available voices:

| Voice | Size | Duration | Latency | Real-time Factor | |-------|------|----------|---------|------------------| | nova | 26,376 B | 3.29s | 1.45s | 2.27x | | onyx | 26,794 B | 3.34s | 1.51s | 2.21x | | echo | 29,929 B | 3.74s | 1.66s | 2.25x | | alloy | 29,511 B | 3.68s | 2.67s | 1.38x | | shimmer | 31,391 B | 3.92s | 2.03s | 1.93x | | fable | 34,944 B | 4.36s | 1.83s | 2.38x |

Piper is 1.3-1.9x slower than Kokoro (which did the same sentence in 1.11s with a 4.99s clip). But it runs on CPU with 22 MiB RAM — it works when the GPU is fully loaded by inference tasks.

Voice Mapping

openedai-speech maps OpenAI voice names to Piper voices:

  • alloy, echo, fable, nova, onyx, shimmer — OpenAI-compatible names
  • Under the hood: en_GB-northern_english_male-medium, en_US-libritts_r-medium Piper models

Production Architecture

Our YouTube pipeline uses all three services in a fallback chain:

# youtube-pipeline/tts_processor.py
DEFAULT_PRIMARY_URL = "http://localhost:8880"    # Kokoro (GPU)
DEFAULT_FALLBACK_URL = "http://localhost:8002"   # openedai-speech (CPU)

# Flow: Script (LiteLLM) → TTS (Kokoro) → ffmpeg assembly → STT (Speaches) → SRT

The fallback chain means TTS never fails. If the GPU is overloaded, Piper picks up the slack on CPU. Speaches runs after assembly — transcribing the final audio for SRT subtitles.

Docker Compose Setup

# docker-compose.yml (speech services)
services:
  kokoro:
    image: ghcr.io/remsky/kokoro-fastapi-gpu:latest
    ports:
      - "8880:8880"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    restart: unless-stopped

  speaches:
    image: ghcr.io/speaches-ai/speaches:latest-cuda
    ports:
      - "8001:8000"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    restart: unless-stopped

  openedai-speech:
    image: ghcr.io/matatonic/openedai-speech:latest
    ports:
      - "8002:8000"
    restart: unless-stopped

All three services are OpenAI API-compatible. No SDK changes needed — just change the base URL.

Lessons From Three Months in Production

1. Kokoro is fast enough for interactive use. Sub-400ms generation for short clips means you can use it for real-time voice agents, not just batch processing.

2. Speaches is slow on short clips, fine on long ones. The 10.96s latency on a 5s clip looks bad, but it's model loading + inference. On a 10-minute audio file, it transcribes in ~30s — well within batch processing tolerance.

3. Piper is the safety net. 22 MiB RAM, CPU-only, no GPU dependency. When Kokoro is down for updates or the GPU is saturated, Piper keeps the pipeline running. Quality is noticeably lower (more robotic), but the pipeline doesn't break.

4. OpenAI API compatibility is the killer feature. We didn't write any TTS/STT-specific code. We use the standard openai Python library with base_url pointed at localhost. When we tested ElevenLabs briefly, the only change was the URL and API key.

5. GPU memory matters. Kokoro uses 248 MiB, Speaches uses 1.97 GiB. On our 121.7 GiB GB10, that's negligible. On a smaller GPU (8 GiB), you'd need to be more careful about model loading order — load Whisper after your LLM, not before.

6. Subtitle generation is free. Speaches transcribes our TTS output to generate SRT files. The accuracy is near-perfect because the input is clean TTS audio — no background noise, no accents, no music. We get 100% accuracy subtitles for zero cost.

Cost Comparison

| Service | Monthly Cost | Per-Request Cost | Latency | Quality | |---------|-------------|-----------------|---------|---------| | ElevenLabs (Turbo) | €22-99/mo | $0.30/1k chars | 200-400ms | Excellent | | OpenAI TTS | Pay per use | $0.015/1k chars | 300-600ms | Good | Kokoro (self-hosted) | €0 (electricity) | €0 | 350ms-1.1s | Very Good | | openedai-speech (Piper) | €0 (electricity) | €0 | 1.4-2.7s | Fair | | Speaches STT (self-hosted) | €0 (electricity) | €0 | ~11s (5s clip) | Very Good |

The electricity cost for running these three containers is negligible — the GPU is already powered for LLM inference. These voice services piggyback on existing infrastructure.

What's Missing

  • Voice cloning — Kokoro doesn't support custom voices. For a branded voice, fine-tune separately.
  • Streaming TTS — Kokoro generates the full clip before returning. For token-by-token audio, look at Parler-TTS.
  • Multi-speaker — Single-speaker only. For dialogue, generate each line separately and stitch with ffmpeg.
  • Emotion control — No emotion parameters via API. You'd need a model with emotion conditioning.

The Verdict

If you're running a content pipeline — YouTube, podcast, blog-to-audio — and you have a GPU, self-hosting TTS/STT is a no-brainer. The quality is good enough for production, the latency is competitive with cloud APIs, and the cost is zero.

Kokoro is the primary TTS. Speaches handles STT for subtitles. openedai-speech (Piper) is the fallback that keeps the pipeline alive when everything else fails.

Three containers, zero API keys, zero per-request costs. That's sovereign voice.


Want to build a self-hosted voice pipeline? Agendar Consultoría Inicial — VORLUX AI designs and deploys sovereign AI infrastructure for European enterprises.

This stack runs on a DGX Spark (NVIDIA GB10, 121.7 GiB RAM) in Alboraya, Valencia. All benchmarks measured on 2026-08-23 with containers running for 9 days uninterrupted.

ttssttopen-sourcekokorospeachesself-hostedvoicesovereign-ai