TTS Providers
A provider is a text-to-speech service that OpenReader sends text to. Admins manage them in Settings → Admin → Providers, which lists Local models (run by the compute worker itself) above External providers (services reached over the network). Everyone on the instance can use the enabled ones. Users pick a provider, model and voice in Settings → Model & Voice, and can choose a different voice for each book from the player (see Reading and Listening).
API keys stay on the server, encrypted (see How saved keys are protected). Users never see them. Speech is generated by the compute worker, so the worker is the one that connects to each provider.
Provider types
| Type | What it is | Needs |
|---|---|---|
| Kokoro (local model) | Kokoro-82M running inside the compute worker. English (28 American and British voices), Spanish, Hindi, Italian and Brazilian Portuguese (12 voices); voices can be blended. | Nothing; the model and selected voice's pronunciation data download the first time it speaks. |
| Custom OpenAI-like | Any server with an OpenAI-compatible speech endpoint, such as Kokoro-FastAPI, KittenTTS-FastAPI, Orpheus-FastAPI or Supertonic. | Base URL; API key if the server requires one. |
| OpenAI | OpenAI's speech API (tts-1, tts-1-hd, gpt-4o-mini-tts). | API key. |
| Replicate | Hosted models on Replicate, from a built-in list or any model ID. | API key. |
| Google AI Studio | Gemini 3.8 Flash TTS and Flash-Lite TTS, with style instructions and 30 prebuilt voices. | Google AI Studio API key. |
| Fish Audio | S2.1 Pro Free, S2.1 Pro, S2 Pro and Drama 3, with style instructions and voice cloning. | API key. |
| OpenRouter | Every speech model in OpenRouter's catalog, including free variants, through one key. | API key. |
Setup guides for specific services are listed under TTS Providers in the sidebar.
Local models
Each local model has a card under Local models. Turn on Offer Kokoro-82M to users to add it to everyone's provider list; turn it off to hide it again. Set as default makes it the default provider for new users.
Kokoro voice data
The Kokoro card shows whether its voice data is downloaded. From there you can download it ahead of time, cancel a download, or remove the data. With several worker replicas, the card manages only the replica that answers.
On a fresh installation, Download prepares the model, the default af_heart voice, and US pronunciation data (about 319 MB). It joins any download already running and reuses any complete installed voice. Other voice packs and language pronunciation data download on first use; a blend downloads each selected voice pack and uses its first voice's language. Once any supported voice and its pronunciation data are complete, the card shows Ready. Missing optional languages do not make it partly downloaded. The downloaded size and Remove confirmation include all installed voice and pronunciation data.
Pronunciation uses a built-in port of Misaki without espeak, so a few abbreviations (for example St.) are skipped rather than read.
Add an external provider
Under External providers, choose Add provider, pick the service, and fill in:
| Field | Notes |
|---|---|
| Base URL | Only for an OpenAI-compatible server, typically ending in /v1. |
| Display name | What users see in their provider list. Starts as the service's name. |
| Default model | The model users start with. Choose from the service's list, or enter a custom model ID. |
| API key | Get a key opens the service's key page. When editing, leave it blank to keep the saved key. |
| TTS instructions | Only for models that accept speaking instructions, such as gpt-4o-mini-tts. |
| Model options | OpenRouter only. Default JSON options sent with the model's requests; users can replace them (see OpenRouter). |
| Slug (Advanced) | Generated from the display name. Users' settings store it, so it cannot change after saving. |
| Enabled (Advanced) | Turn off to hide the provider from users without deleting it. |
Choose Check before saving. It checks the settings, connects to the service, lists its voices and, for a saved provider, speaks a short sample through the worker. Each result reads OK, Check or Fix, with what to change.
Each provider's menu can edit, disable, delete it, or set it as the default for new users (also available in Instance Settings).
Self-hosted provider addresses
The compute worker, not the browser, connects to the provider, and the app server connects when it lists voices. A self-hosted base URL must be reachable from both:
| Setup | Base URL |
|---|---|
Native pnpm dev / pnpm start, provider on the same machine | http://127.0.0.1:<port>/v1 |
| Single Docker container, provider on the Docker host | http://host.docker.internal:<port>/v1 (on Linux, add Docker's host-gateway mapping) |
| Docker Compose | The provider's service name, such as http://kokoro-tts:8880/v1 |
| Remote worker (Railway or another host) | A public, private-network or VPN address reachable from both the app and the worker |
localhost always means the current container or process. A remote worker cannot reach a provider on your laptop through it.
Custom server requirements
A custom server needs only POST /v1/audio/speech. It may return MP3, WAV, OGG or FLAC; OpenReader converts other formats to MP3, so the server does not need to support response_format. An API key is optional.
Voice listing is optional. OpenReader tries /v1/audio/voices, /v1/voices and /v1/styles. If none answer, it offers the Kokoro voices for Kokoro models and the standard OpenAI voices (alloy, echo, fable, onyx, nova, shimmer) otherwise.
For external providers, Custom voice ID below the voice list lets users enter an ID supplied by their provider, even when it is missing from the catalog. They can preview it, choose Use voice, then Save in settings or Apply in the reader. The provider must support the entered ID.
Personal voices
With Fish Audio, or an OpenRouter model whose endpoints support voice cloning, users can create their own voice from a short recording.
Voice cloning has no OpenReader purchase gate on web. The iOS app keeps its Pro requirement; provider charges are separate on both platforms. The provider receives the private sample with each narration request. OpenReader never creates a saved voice on the provider's side; how long the provider keeps the sample depends on its own data policy. Web samples stay in your instance's private storage. iOS samples stay on that device and do not sync through iCloud. Removing a saved voice deletes its sample and keeps cached narration.
Seed a provider from the environment
On first start, if there are no providers yet and API_BASE or API_KEY is set, OpenReader creates a Custom OpenAI-like provider with the slug default-openai, using API_BASE, API_KEY and API_MODEL_NAME (default kokoro). After that the variables are no longer read; edit or delete the provider in the admin panel like any other. A providers list in RUNTIME_SEED_JSON replaces this behavior (see Environment Variables).
The Docker Compose examples seed a Kokoro-FastAPI provider, and the desktop app seeds the built-in Kokoro.