AI

Install VoiceStudio: Local Voice Cloning Tested on CPU and GPU

VoiceStudio clones a voice from a short clip and reads any text back in it, on your own hardware, with no account and no per-character bill. The catch shows up the moment you install VoiceStudio on a box without an NVIDIA card: the same seven-second clip that took 2.1 seconds on an RTX 3060 took just under four minutes on a CPU-only server. Your hardware decides whether this tool feels like ElevenLabs or like a batch job.

Original content from computingforgeeks.com - post 177006

You’ll run the official Docker image on a headless Linux server, walk through the first-run wizard in the web UI, generate a first cloned voice, then call the same engine from its OpenAI-compatible API with curl. Along the way you’ll see the disk trap that killed the first pull and the licence terms on the default model. Built and run in October 2026 on an Ubuntu 26.04 VM with VoiceStudio 0.5.6, plus a rented RTX 3060 for the GPU numbers.

What VoiceStudio does, and what it doesn’t give you for free

VoiceStudio is a local speech workbench: a Python backend that loads text-to-speech and speech-to-text models, plus a web and Electron front end on top of it. The app code is AGPL-3.0, and the project positions it as a self-hosted ElevenLabs alternative. Once the stable image is running, these are the workspaces it opens with:

  1. Voice clone: a 5 to 15 second reference clip plus text, rendered in that voice.
  2. Voice design: describe a voice in words instead of supplying audio.
  3. Video dubbing: transcribe, translate and re-voice a video with timing kept.
  4. Stories and Audiobook: multi-voice scripts or a single narrator, exported by chapter.
  5. Transcripts and dictation, backed by Whisper-family models you download separately.

Read the limits of the free part before you build anything on it. The default engine is k2-fsa’s OmniVoice, and its model card licenses the pre-trained weights under CC-BY-NC because of the training data, so the out-of-the-box voice is non-commercial even though the app is AGPL. KittenTTS, the small English engine also offered in setup, is Apache-2.0 on Hugging Face. By default every render also passes through an AudioSeal watermark stage (the backend log prints AudioSeal generator loaded on first use), so generated audio is marked as synthetic. A closed Pro build ($99 a year or $299 once, per voicestudio.sh) adds multi-device features but doesn’t change the model licences.

VoiceStudio hardware requirements, measured

You need an x86_64 machine with about 25 GB of free disk for the Docker route, 8 GB of RAM (the render peaked at 3.4 GiB, so that leaves headroom), and an NVIDIA GPU if you want generation to feel interactive. The published Linux packages and images are amd64 only. Every API row in the table below is the same request (the bundled demo voice reading one 22-word sentence) on both test machines:

MeasurementCPU-only VMRTX 3060 12 GB
Hardware8 vCPU on an Intel Core i7-6700, 16 GB RAMRented RTX 3060, 12 GB VRAM
/health devicecpucuda (NVIDIA GeForce RTX 3060)
First clone render (cold)295.6 s in the UI (same sentence, queued behind the welcome clip)49.8 s via API
Warm clone render, 7.6 s of audio3 min 58 s2.1 to 2.3 s
KittenTTS, 14.3 s of audio15.8 s warmnot tested
Peak memory while rendering3.4 GiB RAM (container)2.4 GiB VRAM
Image on disk17 GB (5.74 GB download)same image
OmniVoice + KittenTTS weights3.2 GBsame weights

Read the warm row first, because that is what you live with after setup. On the GPU, OmniVoice rendered 7.6 seconds of speech in about 2 seconds, faster than real time. On the CPU it needed roughly 31 seconds of compute for every second of audio, with all 8 vCPUs pinned near 800% the whole time. The i7-6700 is a 2015 quad-core, so a current desktop CPU will do better, but not by the hundredfold gap to the 3060. KittenTTS is the honest CPU option. It runs at about real time on the same old box, but it is English only, with preset voices instead of cloning.

Memory is the easy part. The container idled at 510 MiB and peaked at 3.4 GiB while rendering, and on the GPU the whole job used 2.4 GiB of VRAM, so an 8 GB card is plenty for the default engine. Disk is where people get caught, and the Docker section covers it.

Pick how to install VoiceStudio

Four install methods ship with the project, and the right one depends on whether your machine has a screen. Docker is the path tested here, because it runs the same backend on a headless server and serves the UI to any browser on your network:

  1. Docker image ghcr.io/debpalash/voicestudio:stable: headless servers and GPU boxes. Recommended for anything you reach over SSH.
  2. AppImage or .deb from the GitHub releases page: a Linux desktop with a graphical session.
  3. The voicestudio.sh/install shell script: downloads the latest AppImage and checks it against SHA256SUMS.txt.
  4. Source build with Bun, uv and Rust: only if you want to run main.

Everything below assumes Docker Engine is already installed. If it isn’t, follow the Docker CE install for Ubuntu 26.04 first, then come back. The lab VM ran Docker 29.9.2 from Docker’s own apt repository, and the commands below assume your user is in the docker group; prefix them with sudo otherwise.

Give Docker enough disk before you pull

Plan for a 5.74 GB download that unpacks to 17 GB, and on a current Docker install it doesn’t land where most people look. The lab VM had a separate 80 GB disk mounted on /var/lib/docker and a 20 GB root, and the first pull still died after eight minutes because the image went to the root disk. Check which image store your Docker uses:

docker info --format '{{.DriverStatus}} {{.DockerRootDir}}'

A fresh Docker 29 install reports the containerd snapshotter:

[[driver-type io.containerd.snapshotter.v1]] /var/lib/docker

With io.containerd.snapshotter.v1, image layers live under /var/lib/containerd, not under the Docker root directory the second field shows. So the filesystem that needs the space is whichever one holds /var/lib/containerd. If the first field says overlay2 instead (common on hosts upgraded from older Docker releases), layers live under the Docker root directory, so check that path with df instead. Confirm it has at least 25 GB free:

df -h /var/lib/containerd

If it’s on a small root disk and you have a bigger one mounted at /var/lib/docker, bind-mount a directory from the big disk over /var/lib/containerd. Stop Docker and containerd first so nothing writes to the store while it moves:

sudo systemctl stop docker docker.socket containerd
sudo mkdir -p /var/lib/docker/.containerd
sudo rsync -a /var/lib/containerd/ /var/lib/docker/.containerd/
echo "/var/lib/docker/.containerd /var/lib/containerd none bind 0 0" | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount /var/lib/containerd
sudo systemctl start containerd docker

Run df -h /var/lib/containerd again. It should now report the large disk (75 GB free on the lab VM) instead of the root filesystem. The old contents stay on the root disk, hidden under the mount point, which costs nothing after a failed pull but is worth cleaning up if you already had images. Docker’s documented alternative is setting root in /etc/containerd/config.toml (see Docker’s data directory notes), but the bind mount is what was tested here. The models volume (3.2 GB) still lives under /var/lib/docker/volumes, so that filesystem needs room too. Skip this whole step if the root disk already has the space.

Error: “failed to extract layer … no space left on device”

A failed pull prints this after downloading most of the image. Docker cleans up the partial layers afterwards, so df shows plenty of free space again and the cause is easy to miss:

failed to extract layer (application/vnd.oci.image.layer.v1.tar+gzip sha256:ca288f585926e8c941726c9f5c56b79ca043cb4c471473661d99c15edb46f9de) to overlayfs as "extract-537342638-0q4M sha256:6bc13864d24ad13ab569c00d8a941961502ef822b98cfdd3d12cbd8bbe505ec2": write /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/13/fs/opt/conda/lib/python3.11/site-packages/cudnn8_compat/nvidia/cudnn/lib/libcudnn_adv_train.so.8: no space left on device

Look at the path in the message, /var/lib/containerd/..., rather than at the Docker root directory. The root disk had 15 GB free, which is not enough for a 17 GB unpack, and adding space under /var/lib/docker does nothing for it because containerd never writes there. Free up or move the containerd filesystem as shown above, then pull again.

Pull the VoiceStudio Docker image

Use the :stable tag. The :latest tag follows the main branch and can be ahead of the last release, which is not what you want on a box you’ll come back to:

docker pull ghcr.io/debpalash/voicestudio:stable

On the lab connection the pull took 8 minutes 19 seconds. Check the size once it finishes, because you will want to know what you just committed to.

docker images ghcr.io/debpalash/voicestudio

Expect the content size and the unpacked disk usage to differ by roughly three times:

IMAGE                                  ID             DISK USAGE   CONTENT SIZE   EXTRA
ghcr.io/debpalash/voicestudio:stable   588fe119082b         17GB         5.74GB

The image carries the CUDA build of PyTorch and NVIDIA’s libraries (the failed pull above died unpacking cuDNN), and there’s no separate CPU image, so a CPU-only server pays the full 17 GB too.

Start the VoiceStudio container

You need an administrator API key even when you only publish it on localhost, because Docker’s NAT stops the backend from proving a request came from the host. Generate a random key and keep a copy in a file only your user can read, since you’ll paste it into the browser and the API later:

export OMNIVOICE_API_KEY="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
echo "$OMNIVOICE_API_KEY" > ~/.voicestudio-key
chmod 600 ~/.voicestudio-key

Now start the container. Port 3900 is published on 127.0.0.1 only, so nothing on your network can reach it yet. Don’t skip the OMNIVOICE_DATA_DIR line, even though the docker run examples in the upstream Docker guide leave it out. Without it the backend keeps its database, voices and settings in /root/.omnivoice inside the container (the first lab run logged its database backup there), so the next docker rm or upgrade wipes them and only the models survive. The project’s own Compose file sets it, which puts everything in the omnivoice-data volume:

docker run -d --name omnivoice \
  -p 127.0.0.1:3900:3900 \
  -e OMNIVOICE_API_KEY="$OMNIVOICE_API_KEY" \
  -e OMNIVOICE_DATA_DIR=/app/omnivoice_data \
  -v omnivoice-data:/app/omnivoice_data \
  ghcr.io/debpalash/voicestudio:stable

The backend answers within a second but keeps loading PyTorch and migrating its SQLite database in the background, and /health returns 503 until that’s done. On the lab VM it went healthy after 10 seconds:

curl -s http://127.0.0.1:3900/health

The device field is the one to check. It tells you which hardware every render will use. Once it’s healthy, confirm the data landed in the volume with docker exec omnivoice ls /app/omnivoice_data: you should see omnivoice.db, voices and prefs.json, and huggingface appears after the setup wizard downloads its first model.

{"status":"ok","device":"cpu","version":"0.5.6"}

Image size, container status and health, side by side in one terminal on the lab VM:

VoiceStudio Docker image size, healthy container and health endpoint on Ubuntu 26.04

If device says cpu on a machine that has an NVIDIA card, the container was started without GPU access. The GPU section near the end shows the flag that fixes it.

Open the web UI and finish setup

Because the port is bound to localhost, the simplest way to reach the UI from your laptop is an SSH tunnel. Replace the user and address with your server’s, leave the command running, and open http://localhost:3900 in your browser:

ssh -N -L 3900:127.0.0.1:3900 [email protected]

Your browser first shows a bare “Enter API key” prompt. Paste the key from ~/.voicestudio-key and click Connect; the browser swaps it for a short-lived session and doesn’t store the key itself. A setup wizard follows, and on a server it’s quick:

  1. Pick a UI scale (100% is fine on a 1440-pixel-wide window).
  2. Read the system check, then click Continue (with warnings) if the only warning is the missing GPU.
  3. Download the required VoiceStudio TTS model (OmniVoice, 2.4 GB). Add KittenTTS (0.1 GB) if you’re on CPU.
  4. Answer the anonymous-stats prompt. “No thanks” keeps analytics off.
  5. Skip the dictation step and click Enter studio. Dictation needs a separate speech-to-text model and a desktop shortcut.

Setup’s system check probes RAM, disk, the Python runtime, Hugging Face reachability and the GPU. On the CPU-only VM it flagged exactly one thing in orange, “No compatible GPU detected”, and routed the OmniVoice engine to the CPU:

VoiceStudio setup wizard system check showing CPU-only GPU warning and 15.6 GB RAM

Model downloads come straight from Hugging Face, and no token is needed for the defaults. OmniVoice finished in 3 minutes 30 seconds on the lab connection, while KittenTTS took under a minute.

VoiceStudio models and engines step downloading the 2.4 GB OmniVoice TTS model

Both models land in the omnivoice-data volume, under /app/omnivoice_data/huggingface inside the container, so recreating the container later doesn’t download them again.

Generate your first cloned voice

Every new install gets a “VoiceStudio Demo Voice” profile, a 7-second clip with its transcript already filled in, so you can test cloning before recording anything yourself. On the Launchpad, click Try it next to the demo voice prompt. That opens the Voice workspace with the demo profile selected:

  1. Replace the suggested text in the Text box with your own sentence.
  2. Leave Reference audio on “Using profile: VoiceStudio Demo Voice” and the language on English.
  3. Click Synthesize Audio and wait for the clip to appear under History.

On the CPU VM the backend logged 295.6 seconds for the first render, of which only 237.4 seconds was synthesis, so part of that wasn’t your job at all. Entering the studio for the first time quietly renders a welcome clip (“Welcome to your studio…”), which took 92 seconds on CPU, and with a single CPU worker your first clone waits behind it. The first render also downloads the AudioSeal watermark model. Both are one-time costs, so judge speed from the second render onward:

VoiceStudio voice clone workspace with the first generated clip in history after 295 seconds on CPU

To clone your own voice, click New voice, upload 5 to 15 seconds of clean speech and type exactly what the clip says into its transcript. That transcript matters for speed: a profile without one makes the backend run a Whisper pass to work it out, which on CPU adds noticeably to the first render. Only clone voices you have permission to use.

Use the Model Catalogue to check what’s actually loaded and on which device. After setup it showed OmniVoice active on CPU, KittenTTS resident in memory and dictation not installed:

VoiceStudio model catalogue listing OmniVoice active and KittenTTS in memory on CPU

Other engines in the catalogue (CosyVoice 3, VoxCPM2 and more) show “Needs setup” and pull their own packages and weights when installed, so check each one’s licence the same way before you use its output.

Generate speech from the VoiceStudio API

Scripts don’t need the UI at all. The backend exposes an OpenAI-compatible POST /v1/audio/speech endpoint on the same port, so scripts and existing OpenAI SDK code can call it. In a new shell on the server, load the key back from the file you saved earlier:

export OMNIVOICE_API_KEY="$(cat ~/.voicestudio-key)"

The voice field takes a profile ID, and the demo profile’s ID is demo0001 (list yours with GET /profiles). This request clones the demo voice and writes a WAV file:

time curl -s http://127.0.0.1:3900/v1/audio/speech \
  -H "Authorization: Bearer $OMNIVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "omnivoice", "voice": "demo0001", "input": "This voice was generated on a CPU-only Ubuntu server. No cloud account, no API bill, and the audio never left the machine.", "response_format": "wav"}' \
  -o first-voice.wav

With the model already warm from the UI, the CPU VM still needed almost four minutes:

real	3m57.920s
user	0m0.070s
sys	0m0.079s

Check that you got real audio and not a JSON error saved with a .wav name:

file first-voice.wav

A good result is 24 kHz mono PCM, which is what OmniVoice produces.

first-voice.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 24000 Hz

For quick English drafts on a CPU box, switch the model to kittentts and the voice to default. The same sentence came back in 15.8 seconds on a warm run, as 14.3 seconds of audio. It isn’t a clone of anyone, but it’s usable while you wait for GPU time:

curl -s http://127.0.0.1:3900/v1/audio/speech \
  -H "Authorization: Bearer $OMNIVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "kittentts", "voice": "default", "input": "This voice was generated on a CPU-only Ubuntu server. No cloud account, no API bill, and the audio never left the machine.", "response_format": "wav"}' \
  -o kitten-voice.wav

OpenAI model names are accepted too, such as tts-1, and voice names such as alloy, which map to the active engine and its default voice. Existing code that targets OpenAI’s speech API can point its base URL at the server and keep working.

Run VoiceStudio on an NVIDIA GPU

On a GPU host the only change is the --gpus all flag, and the host needs a working NVIDIA driver plus the NVIDIA Container Toolkit. If nvidia-smi doesn’t work on the host yet, set up the NVIDIA driver and CUDA on Ubuntu first, then install the toolkit from NVIDIA’s Container Toolkit guide. The upstream Docker guide’s check that both are working is docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu22.04 nvidia-smi. If that fails, VoiceStudio can’t see the card either. Load the key with export OMNIVOICE_API_KEY="$(cat ~/.voicestudio-key)" if this is a new shell, remove the CPU container, then start it again with GPU access:

docker rm -f omnivoice
docker run -d --name omnivoice --gpus all \
  -p 127.0.0.1:3900:3900 \
  -e OMNIVOICE_API_KEY="$OMNIVOICE_API_KEY" \
  -e OMNIVOICE_DATA_DIR=/app/omnivoice_data \
  -v omnivoice-data:/app/omnivoice_data \
  ghcr.io/debpalash/voicestudio:stable

Your voices, settings and models stay in the volume, so nothing downloads again. Apart from the data directory line, that command comes from the project’s Docker guide, and the lab VM has no GPU to run it on. The RTX 3060 figures in this article came from the same :stable image on a rented GPU instance, with the backend started by the image’s own uvicorn entrypoint. There, /health reported the card by name:

{"status":"ok","device":"cuda (NVIDIA GeForce RTX 3060)","version":"0.5.6"}

The backend also sized its GPU worker pool from free VRAM: with 11.5 GB free and 5.0 GB budgeted per job, it started 2 workers, so a 12 GB card runs two renders at once. Three identical API requests then took 49.8 s (cold, including the model load and the watermark download), 2.3 s and 2.1 s. AMD cards use a separate :stable-rocm image with --device /dev/kfd --device /dev/dri instead of --gpus, which wasn’t tested here.

If you keep VoiceStudio on a home server and want it from a laptop without opening the port, putting both on a Tailscale tailnet is the route the project itself recommends over exposing plain HTTP. It pairs well with a local LLM box from the Ollama and Open WebUI setup or an image generator like ComfyUI with Stable Diffusion on the same GPU.

VoiceStudio Docker reference card

Keep these day-two commands for the container you just set up. Your voices, history and models all live in the omnivoice-data volume, so every row except the last keeps them:

TaskCommand
Check health and devicecurl -s http://127.0.0.1:3900/health
Follow the backend logdocker logs -f omnivoice
Watch CPU and RAM while renderingdocker stats omnivoice
List voice profilescurl -s -H "Authorization: Bearer $OMNIVOICE_API_KEY" http://127.0.0.1:3900/profiles
Upgrade to a new releasedocker pull ghcr.io/debpalash/voicestudio:stable, then docker rm -f omnivoice and rerun the same docker run
Stop or startdocker stop omnivoice / docker start omnivoice
Remove everything, models and image includeddocker rm -f omnivoice && docker volume rm omnivoice-data && docker rmi ghcr.io/debpalash/voicestudio:stable

Whether VoiceStudio is worth keeping comes down to the hardware row of the first table. With an NVIDIA card it’s a fast, private ElevenLabs alternative for personal and non-commercial work. On CPU alone, treat OmniVoice as an overnight batch tool and use KittenTTS for anything you need now.

Keep reading

Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) AI Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) Open Source LLM Comparison Table (2026) AI Open Source LLM Comparison Table (2026) OpenCode CLI Cheat Sheet: Commands, Sessions and Fixes (PDF) AI OpenCode CLI Cheat Sheet: Commands, Sessions and Fixes (PDF) CapRover Docker Install on Debian 13: Deploy Your First App Containers CapRover Docker Install on Debian 13: Deploy Your First App Best OpenShift Books for Admins and EX280 Candidates Books Best OpenShift Books for Admins and EX280 Candidates OpenCode vs Claude Code vs Cursor vs Codex CLI Compared (2026) AI OpenCode vs Claude Code vs Cursor vs Codex CLI Compared (2026)

Leave a Comment

Press ESC to close