Kokoro-FastAPI with Docker: A Local OpenAI-Compatible Text-to-Speech Server (Linux, Windows, Mac)
Before you start
- 20 min read
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
Kokoro-FastAPI is a free Docker wrapper that turns the small Kokoro-82M voice model into a text-to-speech server with the same speech API as OpenAI, so your text is read aloud on your own machine and never sent to a cloud service.

Logo: the official Kokoro-FastAPI project, README header image (github.com/remsky/Kokoro-FastAPI). The project has no separate logo file.
You point any tool that speaks the OpenAI speech API at http://<your-server>:8880/v1, and it gets spoken audio back. The container also serves a small web player and a Swagger page. People use it for read-aloud in Open WebUI, audiobooks and captions with word timings. The README says the images come with the model baked in, so there is no separate model download.
| What | Value (official docs, read October 2026) |
|---|---|
| Current version | Kokoro-FastAPI v0.9.2, released October 2026 (latest release) |
| Images to pin | ghcr.io/remsky/kokoro-fastapi-cpu:v0.9.2 and ghcr.io/remsky/kokoro-fastapi-gpu:v0.9.2 |
| Time needed | About 15 minutes (our estimate, not measured) |
| Image size | CPU about 1.5 GB, GPU about 4.6 GB compressed (registry data, amd64) |
| RAM | Not documented for CPU; the README shows 3.1 to 4.0 GB of GPU memory in use |
| Port | TCP 8880 (API at /v1, web player at /web, docs at /docs) |
| Code licence | Apache-2.0 (LICENSE at the release tag) |
| Model licence | Kokoro-82M weights: Apache-2.0 (model card on Hugging Face) |
| Official docs | the repository README and its docs folder |
At a glance:
- What it is: one container with an OpenAI-style
/v1/audio/speechendpoint, a web player, voice mixing and caption timings. - CPU or GPU: the CPU image runs anywhere Docker runs. The NVIDIA GPU image is for Linux and for Windows with WSL 2. Mac uses the CPU image.
- No login: the API has no API key or password. Keep it on
127.0.0.1or behind a proxy that asks for one. - Pin the tag: the README says
:latestexists but asks you to pin a release tag. - Languages: English (US and UK), Spanish, French, Hindi, Italian, Japanese, Brazilian Portuguese and Mandarin Chinese.
- Related guides: this is not the project from our older Kokoro Web guide. For speech-to-text see Speaches; for the chat page see Open WebUI with Ollama.
How it fits together#
Apps send text and a voice name to one API port. The container cleans and splits the text, the baked-in Kokoro model speaks it, and audio comes back.
Drawn for this guide from the official README and configuration docs. The dashed HTTPS route is optional.
Before you start#
| CPU image | NVIDIA GPU image | |
|---|---|---|
| Runs on | Linux, Windows (Docker Desktop), Mac (Docker Desktop); amd64 and arm64 | Linux (amd64, arm64), Windows with WSL 2 |
| Hardware | Any recent CPU; RAM not documented | NVIDIA card; GTX 900 series to RTX 40 use the default tag, RTX 50 needs the -cu128 tag |
| Speed | README: about 3.5 s to first audio on an older i7, under 1 s on an M3 Pro | README: about 0.3 s to first audio and 35 to 100 times faster than real time on an RTX 4060 Ti |
| Extra software | Docker | Docker, NVIDIA driver, NVIDIA Container Toolkit (Linux) |
AMD GPUs have an experimental ROCm image that needs a native Linux host (not Docker Desktop or WSL 2); this guide does not cover it. Apple GPU speed (MPS) exists only by running the project directly with uv and start-gpu_mac.sh, not in Docker. We did not test that.
Check Docker on every system:
docker --version docker compose version
On Windows, open PowerShell and run the same two commands. For the GPU image on Linux, nvidia-smi must list your card; on Windows, Docker's docs ask for current NVIDIA drivers, Docker Desktop with the WSL 2 backend, and wsl --update.
Checklist:
- [ ] Docker and Docker Compose work (
docker compose versionprints a version) - [ ] Port 8880 is free on your computer
- [ ] Free disk space well above the compressed image sizes (CPU 1.5 GB, GPU 4.6 GB); we did not measure the unpacked size
- [ ] GPU users: the card shows in
nvidia-smi, and the NVIDIA Container Toolkit is installed on Linux - [ ] For the HTTPS section only: a domain name, a DNS record to your server, and ports 80 and 443 free
Tested by DIY Lab Hub: not run. Every command comes from the official README, docs, Dockerfiles and compose files read in October 2026.
Step 1: Create the folder and the Docker network#
Kokoro-FastAPI keeps no data of its own, because the model and the voices are inside the image. We still use ~/docker_volumes/kokoro-fastapi for the Compose file and its backups. A shared network lets Open WebUI and a proxy reach the container by name.
Linux#
mkdir -p ~/docker_volumes/kokoro-fastapi/backups cd ~/docker_volumes/kokoro-fastapi docker network create kokoro-net
Windows (PowerShell)#
New-Item -ItemType Directory -Force "$HOME\docker_volumes\kokoro-fastapi\backups" | Out-Null Set-Location "$HOME\docker_volumes\kokoro-fastapi" docker network create kokoro-net
Mac (Terminal)#
Same commands as Linux:
mkdir -p ~/docker_volumes/kokoro-fastapi/backups cd ~/docker_volumes/kokoro-fastapi docker network create kokoro-net
Check it worked. docker network ls lists kokoro-net.
Tip: Do not mount folders into the container. The model and voice packs are baked into the image, and a mount over
/app/apihides them (see Common mistakes).
Step 2: Write the Compose file (CPU or GPU)#
This file is based on the project's own compose files, with four changes: the published image pinned to v0.9.2, no source mounts, the port bound to 127.0.0.1, and the shared network. The health check is ours: curl is installed in the image and /health exists in the API code.
Linux#
CPU version:
cd ~/docker_volumes/kokoro-fastapi
cat > docker-compose.yml << 'EOF'
name: kokoro-fastapi
services:
kokoro-fastapi:
image: ghcr.io/remsky/kokoro-fastapi-cpu:v0.9.2
container_name: kokoro-fastapi
restart: unless-stopped
ports:
- "127.0.0.1:8880:8880"
environment:
- API_LOG_LEVEL=INFO
networks:
- kokoro-net
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8880/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
networks:
kokoro-net:
external: true
EOF
NVIDIA GPU version: change the image line to ghcr.io/remsky/kokoro-fastapi-gpu:v0.9.2 (use v0.9.2-cu128 for an RTX 50 card) and add this block under the service, copied from the project's GPU compose file:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
On Linux the GPU also needs the NVIDIA Container Toolkit. After installing it from NVIDIA's guide, run the two commands from that guide:
sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker
Windows (PowerShell)#
CPU version (a here-string keeps the file exact):
Set-Location "$HOME\docker_volumes\kokoro-fastapi"
@'
name: kokoro-fastapi
services:
kokoro-fastapi:
image: ghcr.io/remsky/kokoro-fastapi-cpu:v0.9.2
container_name: kokoro-fastapi
restart: unless-stopped
ports:
- "127.0.0.1:8880:8880"
environment:
- API_LOG_LEVEL=INFO
networks:
- kokoro-net
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8880/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
networks:
kokoro-net:
external: true
'@ | Set-Content -Encoding ascii docker-compose.yml
For the GPU version, make the same two changes as on Linux (GPU image, deploy block). Docker Desktop needs the WSL 2 backend, an NVIDIA card and current NVIDIA drivers; Docker's GPU page lists no toolkit step for Windows.
Mac (Terminal)#
Same commands as Linux, CPU version only. The README says the Docker GPU image is CUDA-only and does not run on Apple Silicon, so use kokoro-fastapi-cpu.
Check it worked. docker compose config prints the file without errors.
Potential issue: if port 8880 is taken, change the left number only, for example 127.0.0.1:8881:8880.
Step 3: Start it and check it#
The first start pulls the image, which is the slow part.
Linux#
cd ~/docker_volumes/kokoro-fastapi docker compose up -d docker compose ps docker compose logs --tail=40 kokoro-fastapi curl -s http://localhost:8880/health
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\kokoro-fastapi" docker compose up -d docker compose ps docker compose logs --tail=40 kokoro-fastapi Invoke-RestMethod http://localhost:8880/health
Mac (Terminal)#
Same commands as Linux.
Check it worked. The health answer is {"status":"healthy"} (from the project's source code). The log banner shows Model warmed up on cpu, Running on CPU and the number of voice packs; the GPU image shows CUDA: True. After about a minute docker compose ps should report healthy. Open http://localhost:8880/web for the player and /docs for the API pages.
Tip: On the GPU image, read the banner.
CUDA: Falsemeans the container does not see your card, even if the log also shows a harmlessCan't initialize NVMLwarning (issue 527).
Step 4: Make your first speech file#
Linux#
curl -s http://localhost:8880/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"kokoro","input":"Hello from my own server.","voice":"af_heart","response_format":"mp3"}' \
--output hello.mp3
ls -l hello.mp3
Play hello.mp3 with your usual audio player.
Windows (PowerShell)#
$body = @{ model = "kokoro"; input = "Hello from my own server."; voice = "af_heart"; response_format = "mp3" } | ConvertTo-Json
Invoke-WebRequest -Uri http://localhost:8880/v1/audio/speech -Method Post -ContentType "application/json" -Body $body -OutFile hello.mp3
Get-Item hello.mp3
Start-Process .\hello.mp3
Mac (Terminal)#
Same request as Linux, then play it:
curl -s http://localhost:8880/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"kokoro","input":"Hello from my own server.","voice":"af_heart","response_format":"mp3"}' \
--output hello.mp3
afplay hello.mp3
Check it worked. hello.mp3 is larger than zero bytes and plays a spoken sentence. af_heart is the project's default voice. The model names kokoro, tts-1, tts-1-hd and gpt-4o-mini-tts all point to the same Kokoro model.
Tip: The Python snippet in the README uses the official
openailibrary withbase_url="http://localhost:8880/v1"andapi_key="not-needed". Any non-empty key works because the server does not check it.
Voices, speed and formats#
| Setting | How | Notes |
|---|---|---|
| Voice | "voice": "af_bella" | List them with GET /v1/audio/voices. The first letter is the language, the second the gender. |
| Mix voices | "voice": "af_bella(2)+af_sky(1)" | Weights are normalised to a 67 percent and 33 percent mix. |
| OpenAI voice names | alloy, echo, nova, shimmer, and others | Mapped to Kokoro voices in api/src/core/openai_mappings.json. For example alloy becomes am_adam. |
| Speed | "speed": 1.2 | Per request. |
| Format | "response_format" | mp3, wav, opus, flac, aac or pcm. |
| Pause | [pause:1.5s] inside the text | Exactly this form; [pause=1.5] is read aloud. |
The README warns that text cleanup can drop or change some phrases. Add "normalization_options":{"normalize": false} to the request to send your text as written.
Step 5: Connect Open WebUI#
Open WebUI can read answers aloud through any OpenAI-style speech server. Its docs have a Kokoro-FastAPI page, but it is a community contribution (not supported by the Open WebUI team) and still shows an old v0.2.1 tag. Use its settings with the tag from this guide.
Linux#
Join Open WebUI to the same network. In the compose file from our Open WebUI guide, merge these lines into the existing open-webui service and networks: block (keep default so it still reaches Ollama):
services:
open-webui:
networks: [default, kokoro-net]
networks:
kokoro-net:
external: true
Apply it with docker compose up -d in that folder. Then open Open WebUI, click your profile picture, choose Admin Panel, Settings, Audio, and set:
| Field | Value |
|---|---|
| Text-to-Speech Engine | OpenAI |
| API Base URL | http://kokoro-fastapi:8880/v1 |
| API Key | not-needed |
| TTS Model | kokoro |
| TTS Voice | af_heart |
Click Save. If Open WebUI runs directly on the host (not in Docker), use http://localhost:8880/v1.
Windows (PowerShell)#
Same compose change, same Admin Panel settings. Docker Desktop runs both containers, so the name kokoro-fastapi works on the shared network. For Open WebUI outside Docker, use http://localhost:8880/v1.
Mac (Terminal)#
Same as Windows. Docker Desktop and the shared network behave the same way.
Check it worked. In a chat, use the Read Aloud (speaker) button under an answer: you hear the voice you chose. In docker compose logs kokoro-fastapi --tail=20 a POST /v1/audio/speech line appears.
Tip:
localhostinside the Open WebUI container is Open WebUI itself. The project's troubleshooting page listshost.docker.internalfor container-to-host traffic, but with the port bound to127.0.0.1the shared network is the reliable way.
Put it behind HTTPS (with or without Traefik)#
Skip this section if you only use the server on one computer. Use it when a phone or another computer must reach the API over the network. The API has no login, so both routes add a user name and password. Open WebUI keeps using http://kokoro-fastapi:8880/v1 inside Docker, which bypasses the proxy.
With Traefik#
On a server that already runs Traefik v3 with the Docker provider, add labels and remove the host port.
This assumes an external Docker network called proxy and an entrypoint called https (use your own names). Create the network once with docker network create proxy if it is missing. In the Compose file, delete the ports: block, add proxy to networks: (and to the bottom as external), and add these labels:
networks:
- kokoro-net
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.kokoro.rule=Host(`tts.<your-domain>`)"
- "traefik.http.routers.kokoro.entrypoints=https"
- "traefik.http.routers.kokoro.tls=true"
- "traefik.http.routers.kokoro.middlewares=kokoro-auth"
- "traefik.http.middlewares.kokoro-auth.basicauth.users=<user>:<hash-with-every-$-doubled>"
- "traefik.http.services.kokoro.loadbalancer.server.port=8880"
Make the hash with htpasswd -nB <user>, as Traefik's docs show. They say every $ in the hash must be doubled in a Compose file. Then run docker compose up -d.
Check it worked. curl -I https://tts.<your-domain>/health returns 401 without a password, and curl -u <user> https://tts.<your-domain>/health returns the health answer.
Without Traefik: Caddy#
Choose this if you have no Traefik. Caddy gets and renews its own certificates.
Caddy's docs describe automatic Let's Encrypt certificates for a public domain and tls internal for a local certificate at home.
mkdir -p ~/docker_volumes/caddy/{data,config}
cd ~/docker_volumes/caddy
On Windows use New-Item -ItemType Directory -Force "$HOME\docker_volumes\caddy\data","$HOME\docker_volumes\caddy\config". Save as docker-compose.yml in that folder (image caddy:2.11.7 is the latest Caddy release read in October 2026):
services:
caddy:
image: caddy:2.11.7
container_name: caddy
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./data:/data
- ./config:/config
networks:
- kokoro-net
networks:
kokoro-net:
external: true
Make a password hash with Caddy's own image (it asks for the password; works on any system). Caddy's basic_auth accepts hashes only, never plain text:
docker run --rm -it caddy:2.11.7 caddy hash-password
Save the Caddyfile next to the Compose file. For a public domain:
tts.example.com {
basic_auth {
<user> <hash-from-caddy-hash-password>
}
reverse_proxy kokoro-fastapi:8880
}
For a home-only name, add tls internal and use a name your own DNS or hosts file points at the server:
tts.home.arpa {
tls internal
basic_auth {
<user> <hash-from-caddy-hash-password>
}
reverse_proxy kokoro-fastapi:8880
}
Start it with docker compose up -d in the Caddy folder. For the home-only name, each device must trust Caddy's own root certificate. Caddy's docs give these commands for a Caddy container (run them from the Caddy folder):
# Linux docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/root.crt && sudo update-ca-certificates # Mac docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt /tmp/root.crt && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/root.crt
# Windows (the official page writes %TEMP% for cmd.exe; this is the PowerShell form, in an administrator window) docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt "$env:TEMP\root.crt" certutil -addstore -f "ROOT" "$env:TEMP\root.crt"
Firefox and Chrome may keep their own trust store, so import root.crt there too (Caddy's page lists the menu path).
Check it worked. curl -I https://tts.example.com/health shows an HTTP status over HTTPS (401 without the password). A home-only name shows a padlock only after the root is trusted on that device.
Tip: A certificate error usually means the name in the Caddyfile and the DNS record differ, or ports 80 and 443 do not reach the server.
Security and testing#
| Control | How |
|---|---|
| No built-in login | The configuration reference lists no API key or password. Bind to 127.0.0.1 (Step 2) or add the proxy login above. |
| Port exposure | Never forward 8880 on your router. The image and the project's native start scripts listen on all interfaces (HOST defaults to 0.0.0.0). |
| CORS | CORS_ORIGINS defaults to ["*"]. The docs say to narrow it if the port is reachable beyond localhost. |
| Optional routes | /debug/*, /dev/unload and the voice tuner are off by default. Leave them off unless you need them. Issue 478 reported the unload route as unauthenticated; it is now behind the ALLOW_DEV_UNLOAD flag. |
| Size limits | MAX_INPUT_LENGTH (1,000,000 characters) and, new in v0.9.2, MAX_REQUEST_BODY_MB (32). Lower them on a shared server. |
| Voice tuner | Off by default. The README calls it "a tuner, not a cloner" and asks you to use only voices you have permission for. |
What leaves the machine: the image pull from GHCR. The start script runs the model download script, which the docs say exits early when valid model files are baked in. We did not capture network traffic.
Quick tests after each change (Windows: use Invoke-RestMethod in PowerShell):
cd ~/docker_volumes/kokoro-fastapi docker compose ps docker compose logs --tail=100 kokoro-fastapi curl -s http://localhost:8880/health curl -s http://localhost:8880/v1/audio/voices | head -c 300
Licence: the code and the model#
| Part | Licence | Source |
|---|---|---|
| Kokoro-FastAPI code | Apache-2.0 | LICENSE at the v0.9.2 tag; README licence section |
| Kokoro-82M model weights | Apache-2.0 | README ("The Kokoro model weights are licensed under Apache 2.0") and the Hugging Face model card (license: apache-2.0, not gated) |
| Inference code adapted from StyleTTS2 | MIT | README licence section |
The model card says Kokoro was trained only on permissive or non-copyrighted audio and lists two Creative Commons BY sets (Koniwa and SIWIS) used in training. It welcomes use "from production environments to personal projects". This is our reading of the official texts, not legal advice. If you pass the software on, keep the Apache notices. The card also warns that websites with "kokoro" in the domain name are not affiliated with the model; use only the sources linked here.
Common mistakes#
These come from the project's issue tracker, its own docs, and common AI assistant answers.
| Mistake | Why it happens | Do this instead |
|---|---|---|
Keeping the repo's ../../api:/app/api mounts with a published image | The repo's compose file bind-mounts source for development. Issue 521 reports Could not import module "api.src.main"; issue 532 reports a missing pydub | Use no mounts with image:; the docs say mounts "run published deps against local source" |
Using localhost:8880 inside the Open WebUI container | localhost there is Open WebUI itself | Join the shared network and use http://kokoro-fastapi:8880/v1 |
Changing AUDIO_TTS_* variables after Open WebUI's first start | Open WebUI stores these settings internally after the first launch | Change them in Admin Panel, Settings, Audio |
| Publishing 8880 to the whole network or internet | The default compose maps 8880:8880, and the API has no login | Publish on 127.0.0.1; use the proxy with a password for remote use |
Running :latest | It moves with every release | Pin v0.9.2; to roll back, change the tag to v0.9.1 |
| Using the default GPU tag on an RTX 50 card | The default tag ships CUDA 12.6 libraries; Blackwell needs the -cu128 tag | Use kokoro-fastapi-gpu:v0.9.2-cu128 |
Ignoring CUDA: False in the banner | The GPU image falls back to the CPU quietly | Check the banner; issue 527's fix was --group-add for the NVIDIA device group, see Troubleshooting |
| Expecting voice cloning | The optional tuner only moves toward a reference voice | Pick or mix built-in voices; enable the tuner only with permission to use the voice |
AI assistants: guessing an image name such as kokoro-fastapi:latest or a Docker Hub path | The images are kokoro-fastapi-cpu and kokoro-fastapi-gpu on ghcr.io/remsky | Copy the image names from the README or this guide |
AI assistants: copying the old Open WebUI page (tag v0.2.1, "CUDA 12.3", "CPU Onnx") or asking for an API key | That page is old and the server has no key | Use v0.9.2, key not-needed, port 8880 |
Tip: After every compose change, run
docker compose up -dagain. Changing a variable in the file does nothing to a running container (issue 537 was an unrecreated container).
Tip: Test with the web player first (
/web). If it speaks there but Open WebUI is silent, the problem is the connection, not the model.
Troubleshooting#
| Problem | Solution |
|---|---|
Container will not start: Error loading ASGI app. Could not import module "api.src.main" | Remove any ./api:/app/api or ../../api:/app/api volume lines (issues 521, 532) |
| Silent or empty audio for some paragraphs | A paragraph made only of symbols such as .... returns an empty file (issue 552; the fix was promised for a later minor release). Remove those lines or filter them in your app |
| Words missing or changed | Send "normalization_options":{"normalize": false} |
GPU image runs on the CPU (CUDA: False) | Check nvidia-smi and the toolkit; try group_add: ["video"] in Compose (the project's GPU permissions page) or --group-add for the group of /dev/nvidiactl (issue 527) |
Can't initialize NVML warning | Harmless if the banner says CUDA: True (maintainer's answer in issue 527) |
| Open WebUI shows only six voices | Fixed in the API in issue 462; update Kokoro-FastAPI and reload the Audio settings |
| Open WebUI cannot connect | Use the container name on the shared network, and check curl http://localhost:8880/health on the host |
docker compose cannot find kokoro-net | Run docker network create kokoro-net once (Step 1) |
| WAV length looks wrong in a script | Python's wave module misreads the streaming header; use soundfile or ffprobe (README) |
Known bugs#
- Issue 552 (closed): symbol-only paragraphs give an empty file; the maintainer said the fix would come in a later minor release, not the 0.9.2 patch.
- Issues 551 and 535 (open): feature requests only (speed through ffmpeg, Roman numerals).
Diagnostics: docker compose ps, docker compose logs --tail=100 kokoro-fastapi, docker inspect kokoro-fastapi and docker stats kokoro-fastapi.
Updates and backups#
Update. Read the project changelog, change the tag in docker-compose.yml, then run (Windows: the same commands in PowerShell):
cd ~/docker_volumes/kokoro-fastapi docker compose pull docker compose up -d docker compose logs --tail=50 kokoro-fastapi
The README says you may have to go back to an earlier tag if something breaks. v0.9.2 is days old, so v0.9.1 is the known earlier tag.
Back up. There is no application data; back up the Compose file (and the Caddy folder if you use it):
cd ~/docker_volumes/kokoro-fastapi tar -czf "backups/kokoro-fastapi-$(date +%F).tar.gz" docker-compose.yml
Run it every night at 03:00 on Linux or Mac with crontab -e and this line (replace <you> with your user name):
0 3 * * * cd /home/<you>/docker_volumes/kokoro-fastapi && tar -czf "backups/kokoro-fastapi-$(date +\%F).tar.gz" docker-compose.yml
On Windows, save these lines as backup.ps1 in the Kokoro folder:
$dir = "$HOME\docker_volumes\kokoro-fastapi" Compress-Archive -Path "$dir\docker-compose.yml" -DestinationPath "$dir\backups\kokoro-fastapi-$(Get-Date -Format yyyy-MM-dd).zip" -Force
Then schedule it once: schtasks /Create /SC DAILY /ST 03:00 /TN "Kokoro backup" /TR "powershell.exe -NoProfile -File $HOME\docker_volumes\kokoro-fastapi\backup.ps1".
Restore test. Unpack into a new folder, never over the live one:
mkdir -p /tmp/restore-test && tar -xzf backups/kokoro-fastapi-<date>.tar.gz -C /tmp/restore-test cd /tmp/restore-test && docker compose config
Performance and resources#
The project publishes these numbers, measured on its author's machine (Windows 11 with WSL 2, RTX 4060 Ti 16 GB, i7-11700, 64 GB RAM). Yours will differ.
| Measure | Documented value |
|---|---|
| Speed on GPU | 35 to 100 times faster than real time on long books |
| First audio, GPU | About 300 ms at chunk size 400 |
| First audio, CPU | About 3,500 ms (older i7) and under 1 s (M3 Pro), chunk size 200 |
| GPU memory | 3.11 GB loaded after a 6-second job; 3.98 GB after a long job |
After /dev/unload | 2.37 GB floor (needs ALLOW_DEV_UNLOAD=true) |
Settings that matter, from the configuration reference:
WEB_CONCURRENCY(default 1): one worker serves one request at a time; each extra worker loads its own model copy, so GPU memory grows.MODEL_AUTO_UNLOAD_TIMEOUT_SECONDS(default 0 = off): frees GPU memory after idle seconds; the next request reloads the model.TARGET_MIN_TOKENS,TARGET_MAX_TOKENS,ABSOLUTE_MAX_TOKENS(175, 250, 450): how text is chunked.API_LOG_LEVEL: the default isDEBUG, which is noisy; this guide setsINFO.
Watch your own numbers with docker stats kokoro-fastapi and, on NVIDIA, nvidia-smi. CPU image RAM is not documented, so measure it on a small server.
FAQ#
Is Kokoro-FastAPI free for commercial use? The code and the model weights are both Apache-2.0 according to the repository and the model card. This is our reading, not legal advice.
How is it different from our Kokoro Web guide? Kokoro Web is a different project, covered in our older guide. Kokoro-FastAPI is the remsky project in this guide, with CPU and GPU images and a documented API.
Can Speaches do the same job? Our Speaches guide covers a server that mainly does speech-to-text and also offers Kokoro for speech. Choose Kokoro-FastAPI for speech alone, with voice mixing and captions.
Does it need a GPU? No. The CPU image works everywhere; the README gives CPU first-audio times. A GPU makes long jobs much faster.
Can I run it on a Mac with Apple GPU speed? Not in Docker. The README offers ./start-gpu_mac.sh for native Apple GPU use, which we did not test. The CPU image works on Apple Silicon.
Sources#
- Kokoro-FastAPI release v0.9.2, README, LICENSE, CHANGELOG, configuration, troubleshooting, the CPU compose file and GPU compose file - read October 2026
- Images on GHCR: kokoro-fastapi-cpu and kokoro-fastapi-gpu (tags checked) - October 2026
- Kokoro-82M model card (licence, training data, voices) - October 2026
- Open WebUI: Kokoro-FastAPI page, OpenAI TTS settings and environment variables - October 2026
- NVIDIA Container Toolkit install guide and Docker Desktop GPU support - October 2026
- Caddy release v2.11.7, Caddy running in Docker, basic_auth and Traefik basicAuth - October 2026
- GitHub issues #462, #478, #521, #527, #532, #535, #537, #551, #552 - read October 2026
Checked against the official docs on 2026-10-31.