In meinem lokalen KI-Stack laufen bereits Inferenz-Server, Open WebUI, der Hermes-Agent, Firecrawl, SearXNG, ComfyUI und noch einige mehr. Alles habe ich auf eigener Hardware, ohne Cloud aufgesetzt. Was bisher als Integration in Open WebUI mir fehlte: Sprache. Ich wollte Open WebUI per Stimme Texte diktieren können (Speech-to-Text) und mir die Antworten vorlesen lassen (Text-to-Speech), und zwar vollständig lokal und auf Deutsch. In dieser Anleitung zeige ich euch genau diesen Weg. Er ist bewusst so aufgebaut, dass ihn auch weniger technisch versierte Leser nachbauen können, um ihren lokalen KI-Stack um das Thema Sprache erweitern zu können.
Das Ergebnis: STT über das in Open WebUI eingebaute Whisper (kein Extra-Dienst nötig) und TTS über einen einzigen Docker-Container namens openedai-speech, der zwei deutsche Stimm-Engines mitbringt.
- Piper (schnell, läuft auf der CPU)
- XTTS v2 (hohe Qualität mit GPU, inklusive Voice-Cloning)
Wichtiger Hinweis zum Projektstatus: Das GitHub-Repository von openedai-speech wurde am 4. Januar 2026 archiviert und steht seitdem auf read-only. Es wird also nicht mehr weiterentwickelt und es sind keine Updates mehr zu erwarten. Die Docker-Images bleiben aber weiterhin über die GitHub Container Registry (ghcr.io) verfügbar, und das hier beschriebene Setup funktioniert stabil.
URL: https://github.com/matatonic/openedai-speech
Warum ich es trotzdem empfehle: Für deutsche Sprachausgabe ist die Auswahl an lokalen, einfach zu betreibenden TTS-Servern nach wie vor dünn. Der aktuelle Community-Favorit Kokoro etwa unterstützt kein Deutsch. Die Kombination aus dem deutschen Piper-Modell „Thorsten“ und XTTS v2 mit Voice-Cloning liefert dagegen sehr gute deutsche Ergebnisse – und genau diese beiden Engines verpackt openedai-speech in einem einzigen Container mit OpenAI-kompatibler API. Solange es keinen gleichwertigen, aktiv gepflegten Ersatz mit guter Deutsch-Unterstützung gibt, bleibt es für diesen Anwendungsfall meine erste Wahl.
Schreibt doch bitte hier in den Kommentaren, wenn ihr ein Nachfolgeprojekt kennt bzw. eine Lösung, die auch für die deutsche Sprache gut funktioniert.
Das Zielbild
Bevor wir loslegen, der Überblick, was wir verbinden:
| Baustein | Aufgabe | Lösung |
|---|---|---|
| STT (Sprache → Text) | Eure gesprochenen Prompts in Text wandeln | Whisper (in Open WebUI eingebaut) |
| TTS (Text → Sprache) | Antworten vorlesen, deutsche Stimme | openedai-speech mit Piper & XTTS |
Das Schöne: openedai-speech bildet die OpenAI-Audio-API nach. Open WebUI bringt für genau diese Schnittstelle bereits eine fertige TTS-Engine mit. Das bedeutet für uns: Wir brauchen also keinen Spezial-Connector, sondern tragen nur eine URL ein, die auf unseren lokalen Service verweist.
Voraussetzungen
- Ein laufendes Open WebUI (z. B. per Docker).
- Docker und Docker Compose auf dem Host.
- Für XTTS zusätzlich eine NVIDIA-GPU mit ~4 GB freiem VRAM und installiertes NVIDIA Container Toolkit. Für die reine Piper-Variante reicht die CPU.
Schritt 1: Speech-to-Text mit eingebautem Whisper
Den einfachsten Teil erledigt Open WebUI selbst, denn es bringt Whisper bereits mit. Ihr müsst nichts zusätzlich installieren.
Geht in Open WebUI auf Admin-Bereich → Einstellungen → Audio und stellt unter Sprache-zu-Text ein:
- Sprache-zu-Text-Engine: Whisper (Lokal)
- STT-Modell: z. B.
Systran/faster-whisper-large-v3für beste Qualität, oder ein kleineres Modell wiesmall/medium, wenn es schneller und sparsamer sein soll. Wichtig: Entweder die Modellgröße (small,medium,large-v3) oder die vollständige Hugging-Face-Repo-ID inklusive Organisation eintragen – ein verkürzter Name wiefaster-whisper-large-v3funktioniert nicht. Mit dem Download-Symbol rechts neben dem Feld ladet ihr das Modell direkt herunter, statt beim ersten Sprach-Prompt zu warten. - Sprache: Deutsch (entweder über die Umgebungsvariable
WHISPER_LANGUAGE=deoder pro Nutzer unter Einstellungen → Audio)
Whisper transkribiert Deutsch ausgezeichnet. Standardmäßig läuft es in eurem Open-WebUI-Container auf der CPU, was für kurze Sprach-Prompts völlig ausreicht – gerade mit den kleineren Modellen wie small oder medium.
Optional: Whisper GPU-beschleunigt betreiben. Wer das große large-v3-Modell flott nutzen will, lässt Whisper auf der GPU rechnen. Dafür sind drei Dinge nötig:
- Auf dem Host müssen der NVIDIA-Treiber und das NVIDIA Container Toolkit installiert sein (das habt ihr bereits, wenn ihr für XTTS in Schritt 2 die GPU durchreicht).
- Statt des Standard-Images nutzt ihr das
:cuda-Image von Open WebUI:ghcr.io/open-webui/open-webui:cuda. Es bringt die CUDA-Bibliotheken mit, sodass das eingebaute faster-whisper automatisch auf der GPU läuft. - Dem Container muss die GPU durchgereicht werden – per Docker Compose mit demselben
deploy:-Block wie beim TTS-Server, oder beidocker runmit dem Flag--gpus all.
So sieht der relevante Ausschnitt einer docker-compose.yml für Open WebUI mit GPU aus:
services:
open-webui:
image: ghcr.io/open-webui/open-webui:cuda
container_name: open-webui
ports:
- "3000:8080"
volumes:
- ./open-webui:/app/backend/data
environment:
- WHISPER_LANGUAGE=de
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Lauft ihr bereits mit dem Standard-Image, könnt ihr gefahrlos auf :cuda wechseln: Container stoppen, Image-Tag in der Compose-Datei ändern, docker compose pull und docker compose up -d – eure Daten bleiben dank des Volumes erhalten. Ob die GPU tatsächlich genutzt wird, prüft ihr nach der ersten Transkription mit nvidia-smi: Dort taucht dann der Open-WebUI-Prozess mit belegtem VRAM auf. Nebeneffekt: Auch die lokalen Embedding-Modelle der RAG-Funktion von Open WebUI profitieren vom :cuda-Image.
Damit ist STT erledigt. Kommen wir zum spannenderen Teil.
Schritt 2: Den TTS-Server openedai-speech aufsetzen
openedai-speech gibt es in zwei Varianten:
- Das Voll-Image (~8 GB) enthält Piper und XTTS. Das nehmen wir, weil wir beide Stimmen abdecken wollen.
- Das Minimal-Image (<1 GB,
:min) kann nur Piper und läuft rein auf der CPU. Ideal für Leser ohne GPU – dazu unten mehr.
Legt zuerst einen eigenen Ordner für den Dienst an und wechselt hinein:
Befehl: mkdir ~/openedai-speech
Befehl: cd ~/openedai-speech
Jetzt muss noch mit dem folgenden Befehl die docker-compose.yml angelegt werden.
Befehl: nano docker-compose.yml
services:
openedai-speech:
image: ghcr.io/matatonic/openedai-speech
container_name: openedai-speech
ports:
- "5050:8000"
volumes:
- ./voices:/app/voices
- ./config:/app/config
environment:
- TTS_HOME=voices
- HF_HOME=voices
# XTTS beim Start vorladen (spart Wartezeit beim ersten Aufruf):
- PRELOAD_MODEL=xtts
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Jetzt die Datei mit Strg + X schließen und das Speichern mit J (bzw. Y bei englischer Spracheinstellung) bestätigen.
Den Docker Container startet ihr jetzt mit dem folgenden Befehl. Der erste Start dauert eine Weile: Das Image ist ~8 GB groß, und anschließend lädt der Container noch rund 2 GB Sprachmodelle (XTTS und die englischen Default-Stimmen) herunter.
Befehl: docker compose up -d
Jetzt könnt ihr mit dem folgenden Befehl prüfen, ob der Container gestartet ist.
Befehl: docker ps --filter name=openedai-speech
Etwas mehr über den Zustand des Containers erfahrt ihr mit dem folgenden Befehl. Bereit ist der Server, sobald in den Logs Uvicorn running on http://0.0.0.0:8000 erscheint – und bei der GPU-Variante sollte dort Loading model xtts to cuda stehen, nicht cpu.
Befehl: docker logs -f openedai-speech
Variante ohne GPU (nur Piper): Lasst den deploy:-Block und PRELOAD_MODEL=xtts weg und nutzt stattdessen das Minimal-Image:
services:
openedai-speech:
image: ghcr.io/matatonic/openedai-speech:min
container_name: openedai-speech
ports:
- "5050:8000"
volumes:
- ./voices:/app/voices
- ./config:/app/config
restart: unless-stopped
Schritt 3: Deutsche Stimmen konfigurieren
Jetzt das Herzstück. openedai-speech ordnet jedem „Stimmnamen“ eine echte Engine zu – und genau hier holen wir Deutsch herein. Die Zuordnung steht in config/voice_to_speaker.yaml. Diese Datei hat der Container beim ersten Start mit einer Default-Konfiguration in euren config/-Ordner geschrieben. Dabei gilt:
- Das Modell
tts-1nutzt Piper (CPU, schnell). - Das Modell
tts-1-hdnutzt XTTS (GPU, hohe Qualität, Voice-Cloning).
Öffnet die Datei mit dem folgenden Befehl (sudo ist nötig, weil der Container die Datei als root angelegt hat):
Befehl: sudo nano config/voice_to_speaker.yaml
Ganz wichtig: Die beiden deutschen Stimmen werden in die bestehenden Abschnitte tts-1: und tts-1-hd: eingefügt – nicht als neue Blöcke ans Dateiende kopiert! Hängt ihr sie als eigene Abschnitte an, findet der Server die Stimmen nicht und ihr bekommt beim Aufruf einen Fehler wie Error loading voice: thorsten, KeyError: 'thorsten'. Genau in diese Falle bin ich beim Einrichten selbst getappt.
Den Eintrag thorsten fügt ihr ans Ende des tts-1:-Abschnitts ein, auf gleicher Einrückungsebene wie die vorhandenen Stimmen (alloy, shimmer, …), also direkt vor der Zeile tts-1-hd::
shimmer:
model: voices/en_US-libritts_r-medium.onnx
speaker: 163
# Deutsche Piper-Stimme (CPU, schnell)
thorsten:
model: voices/de_DE-thorsten-medium.onnx
speaker: # Standardsprecher des Modells
tts-1-hd:
Den Eintrag thomas fügt ihr entsprechend ans Ende des tts-1-hd:-Abschnitts ein (nach dem letzten vorhandenen Stimmen-Block):
# Deutsche XTTS-Stimme (GPU, hohe Qualitaet, optional geklont)
thomas:
model: xtts
speaker: voices/thomas.wav # 6-30 s sauberes Audio-Sample der Wunschstimme
language: de
Achtet penibel auf die Einrückung: Die Stimmnamen stehen mit zwei Leerzeichen eingerückt, die Felder darunter mit vier – YAML ist da unerbittlich. Jetzt die Datei mit Strg + X schließen und das Speichern bestätigen.
Zwei Hinweise zu den Einträgen:
Piper (Eintrag „thorsten“): Hier nutzen wir das frei verfügbare deutsche Thorsten-Modell. Der automatische Modell-Download von openedai-speech funktioniert nur für die mitgelieferten Default-Stimmen – bei eigenen Einträgen läuft Piper sonst in einen VoiceNotFoundError. Ladet die .onnx-Datei und die zugehörige .onnx.json deshalb einmalig manuell von Hugging Face in den voices/-Ordner herunter:
Befehl: cd ~/openedai-speech/voices
Befehl: wget https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/de/de_DE/thorsten/medium/de_DE-thorsten-medium.onnx
Befehl: wget https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/de/de_DE/thorsten/medium/de_DE-thorsten-medium.onnx.json
Wer mag, nimmt stattdessen de_DE-thorsten-high für etwas mehr Qualität (dann auch den Pfad in der YAML anpassen).
XTTS (Eintrag „thomas“): XTTS arbeitet mit Voice-Cloning. Ihr legt eine WAV-Datei mit 6 bis 30 Sekunden klar gesprochenem Deutsch in den Ordner voices/ (hier thomas.wav). Diese Aufnahme bestimmt die Klangfarbe der Stimme, und language: de sorgt dafür, dass sauber Deutsch gesprochen wird. XTTS v2 unterstützt 16 Sprachen, darunter Deutsch. Für einen natürlichen deutschen Klang nehmt ihr am besten ein deutsches Sprachsample als Referenz – das kann auch eure eigene Stimme sein.
Nach Änderungen an der YAML den Container einmal neu starten. Achtung: Durch PRELOAD_MODEL=xtts braucht der Neustart einen Moment – wartet in den Logs wieder auf die Uvicorn running-Zeile, bevor ihr testet.
Befehl: docker compose restart
Schritt 4: TTS in Open WebUI einbinden
Jetzt ist im Backend alles bereit. Also zurück in Open WebUI: Geht unter Admin-Bereich → Einstellungen → Audio, diesmal an den Abschnitt Text-zu-Sprache:
- Text-zu-Sprache-Engine: OpenAI
- API Base URL:
http://openedai-speech:8000/v1– das funktioniert nur, wenn Open WebUI undopenedai-speechim selben Docker-Netzwerk bzw. Compose-Projekt laufen. Laufen die Container in getrennten Compose-Projekten (wie bei mir) oder auf unterschiedlichen Rechnern, nutzt ihr stattdessenhttp://<server-ip>:5050/v1 - API-Schlüssel:
sk-egal(ein beliebiger, nicht-leerer Dummy – einen echten Schlüssel brauchtopenedai-speechnicht, aber Open WebUI akzeptiert kein leeres Feld) - TTS-Modell:
tts-1für Piper odertts-1-hdfür XTTS - TTS-Stimme: der Stimmname aus eurer YAML, also
thorsten(Piper) bzw.thomas(XTTS)
Auf Save klicken und die Seite einmal neu laden.
Schritt 5: Testen
Bevor ihr Open WebUI bemüht, könnt ihr den TTS-Server direkt per curl testen. Der folgende Aufruf erzeugt eine MP3-Datei mit der deutschen Piper-Stimme:
curl http://<server-ip>:5050/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model": "tts-1", "voice": "thorsten", "input": "Hallo, das ist ein lokaler Sprachtest."}' \
-o test.mp3
Kommt eine abspielbare test.mp3 heraus, steht der Server. Prüft die Dateigröße mit ls -lh test.mp3 – sie sollte einige zehn Kilobyte haben. Ist die Datei nur wenige hundert Bytes groß, hat der Server statt Audio eine Fehlermeldung geliefert: Schaut sie euch mit cat test.mp3 an. Ein KeyError bedeutet, dass die Stimme nicht in der YAML gefunden wurde (Schritt 3 prüfen), eine fast leere MP3 mit nur einem ID3-Header deutet auf ein fehlendes Piper-Modell hin (wget-Befehle aus Schritt 3 prüfen).
Für XTTS tauscht ihr einfach "model": "tts-1-hd" und "voice": "thomas" ein – vorausgesetzt, eure thomas.wav liegt bereits im voices/-Ordner. Der erste Aufruf dauert etwas länger, falls das Modell noch nicht vorgeladen ist.
Dann der Test in Open WebUI: Schickt eine Nachricht und klickt bei der Antwort auf das Lautsprecher-Symbol – ihr solltet jetzt eine deutsche Stimme hören. Für den vollen Freisprech-Betrieb aktiviert ihr pro Nutzer unter Einstellungen → Audio die Option Auto-playback und nutzt den Sprachanruf-Modus (das Telefonhörer-Symbol im Chat-Eingabefeld, in der Oberfläche je nach Version als „Voice Call“ bezeichnet). Für STT klickt ihr im Chat einfach auf das Mikrofon und sprecht euren Prompt.
Piper oder XTTS – wann nehme ich was?
Beide Wege funktionieren, sie haben aber unterschiedliche Stärken:
Piper (tts-1) |
XTTS v2 (tts-1-hd) |
|
|---|---|---|
| Hardware | CPU reicht | NVIDIA-GPU, ~4 GB VRAM |
| Geschwindigkeit | Sehr schnell, geringe Latenz | Langsamer, höhere Latenz |
| Qualität | Gut, etwas synthetischer | Sehr natürlich |
| Voice-Cloning | Nein (feste Stimmen) | Ja, aus 6-30 s Audio |
| Lizenz | MIT (auch kommerziell) | CPML (nicht-kommerziell) |
Mein Rat: Nehmt Piper als Standard für den flüssigen, interaktiven Sprachbetrieb – es ist schnell, genügsam und lizenztechnisch unproblematisch. Greift zu XTTS, wenn euch maximale Natürlichkeit wichtig ist oder ihr eine eigene, geklonte Stimme wollt und eine GPU frei habt. Da meine zwei RTX A6000 ohnehin laufen, nutze ich XTTS gern für Vorlese-Inhalte und Piper für die schnelle Konversation. Der Clou: Ihr müsst euch nicht entscheiden – beide Stimmen liegen parallel in derselben voice_to_speaker.yaml und ihr wechselt in Open WebUI einfach über das TTS-Modell.
Fazit
Mit überschaubarem Aufwand habe ich Open WebUI um vollwertige, lokale Sprachfunktionen erweitert: Whisper hört zu, Piper oder XTTS antwortet – alles auf eigener Hardware, auf Deutsch, ohne einen einzigen Cloud-Call. Das passt exakt in den Gedanken der souveränen KI, der sich durch meinen gesamten Stack zieht: Die Daten bleiben hier, die Kontrolle bleibt bei mir.
Dass openedai-speech inzwischen archiviert ist, nehme ich dabei bewusst in Kauf: Das Setup ist in sich geschlossen, läuft stabil und liefert für Deutsch ein Ergebnis, das aktuell gepflegte Alternativen schlicht nicht bieten. Genau das ist auch ein Vorteil lokaler Software – ein archiviertes Projekt funktioniert bei mir morgen noch genauso wie heute, ohne dass ein Cloud-Anbieter den Stecker ziehen kann. Sollte ein würdiger Nachfolger mit guter Deutsch-Unterstützung auftauchen, werde ich hier auf dem Blog darüber berichten bzw. schreibt es mir gerne hier in die Kommentare.
Der Einstieg ist dank des eingebauten Whisper und des einen openedai-speech-Containers niedrig genug, dass ihn auch Einsteiger nachbauen können. Und wer mehr will, hat mit XTTS und Voice-Cloning gleich den Premium-Pfad parat.





Ein toller Guide der leicht zugänglich und verständlich ist. Perfekt für ein kleines Side-Project geeignet. Aktuell half mir noch mein…
Thank you for this great tutorial, could you share n8n workflow and comfyui workflow please?
Hallo Anton, die Meldung besagt das in meinem Beisiel Methoden verwendet werden die veraltet (deprecated) sind. Also müsstest Du die…
Danke für das Tool! Ich habe erst kürzlich angefangen mich mit der Thematik zu beschäftigen und bin für meine Erwartungen…
Hallo, ich habe ihre Anleitung befolgt und bekomme im letzten Schritt leider immer folgende Meldung im Terminal: bash <(wget -qO-…