The E‑Worker VibeVoice Integration Stack (GPU) is a unified, single‑container deployment that exposes a clean HTTP proxy on one public port while keeping Microsoft’s VibeVoice backends private inside. This stack simplifies setup, enforces security, and enables applications—including E‑Worker Soundstage—to integrate seamlessly with high‑quality text‑to‑speech (TTS).
https://www.reddit.com/r/eworker_ca/
Important: The VibeVoice API are ready to use, the E-Worker Soundstage UI is still under development.
Microsoft VibeVoice code and models:
Licensing: Accept and comply with Microsoft’s repo/model licenses.
This image orchestrates upstream VibeVoice without modifying the models.
Disclaimer: E‑Worker is not affiliated with Microsoft.
GPU: NVIDIA with drivers + NVIDIA Container Toolkit.
Base: CUDA 12.4 (NGC PyTorch).
Driver: 550+ recommended.
Architectures: Turing, Ampere, Ada (RTX 20/30/40, A10, L4). Volta (V100) may work.
VRAM (approx.):
Disk (cache):
Docker: 24+.
Quick GPU check:
nvidia-smi
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu24.04 nvidia-smi
docker pull eworkerinc/vibevoice:latest # alias of :full (prebaked)
Persistent cache/state setup:
sudo mkdir -p /mnt/vv-hf /mnt/vv-voices /mnt/vv-state \
&& sudo chown -R "$USER":"$USER" /mnt/vv-hf /mnt/vv-voices /mnt/vv-state
Run both models:
docker run -d --name vibevoice --gpus all \
-p 8745:8745 \
-v /mnt/vv-hf:/root/.cache/huggingface \
-e ENABLE_1_5B=true -e ENABLE_LARGE=true \
-e AUTH_REQUIRED=true \
-e CORS_ENABLED=true -e ALLOWED_ORIGINS='*' \
-v /mnt/vv-voices:/app/voices \
-v /mnt/vv-state:/var/lib/eworker \
eworkerinc/vibevoice:latest
VRAM note:
Run with 1.5B only:
docker run -d --name vibevoice-1_5b --gpus all \
-p 8745:8745 -v /mnt/vv-hf:/root/.cache/huggingface \
-e ENABLE_1_5B=true -e ENABLE_LARGE=false \
eworkerinc/vibevoice:latest
VRAM savings:
Run with Large only:
docker run -d --name vibevoice-large --gpus all \
-p 8745:8745 -v /mnt/vv-hf:/root/.cache/huggingface \
-e ENABLE_1_5B=false -e ENABLE_LARGE=true \
eworkerinc/vibevoice:latest
VRAM trade‑off:
docker run -d --name vibevoice \
-p 80:80 -p 443:443 \
-v /mnt/vv-hf:/root/.cache/huggingface \
-v /mnt/vv-state:/var/lib/eworker \
-e TLS_MODE=acme -e TLS_DOMAIN=voice.example.com -e [email protected] \
-e AUTH_REQUIRED=true -e ENABLE_1_5B=true -e ENABLE_LARGE=true \
eworkerinc/vibevoice:latest
X-API-Key: <key> (printed on first run, persisted under /var/lib/eworker/api.key).RESET_API_KEY=true once or set API_KEY explicitly.CORS_ENABLED + ALLOWED_ORIGINS.TLS_MODE=acme.GET /health → health probe.GET /v1/voice/models → list available models.GET /v1/voice/models/{model_id} → model defaults/hints.GET /v1/voice/voices → list voices (supports ?model= filter).GET /v1/voice/voices/{voice_id}/preview?text=... → quick WAV preview.POST /v1/voice/jobs → start TTS job (returns job_id).GET /v1/voice/jobs → list jobs; filter by model/status.GET /v1/voice/jobs/{job_id} → job status/progress.GET /v1/voice/jobs/{job_id}/result → audio result.POST /v1/voice/jobs/{job_id}/cancel → cancel job.GET /v1/voice/jobs/metrics → aggregated job metrics.Health check:
curl -s http://localhost:8745/health | jq .
Get API key:
KEY=$(docker logs vibevoice 2>&1 | sed -n 's/^X-API-Key: //p' | tail -1)
List voices:
curl -s http://localhost:8745/v1/voice/voices -H "X-API-Key: $KEY" | jq
# If only Large is enabled, include the model filter:
curl -s "http://localhost:8745/v1/voice/voices?model=vibevoice-large" -H "X-API-Key: $KEY" | jq
Preview a single voice:
curl -s "http://localhost:8745/v1/voice/voices/Alice/preview?text=Hello" \
-H "X-API-Key: $KEY" --output preview.wav
Two‑speaker dialogue (very small):
cat > body.json <<'JSON'
{
"model": "vibevoice-1.5b",
"script": "Speaker 1: Hello there!\nSpeaker 2: Hi! Great to meet you.",
"speakers": [ { "voiceName": "Alice" }, { "voiceName": "Carter" } ],
"overrides": {
"guidance": { "inference_steps": 28, "cfg_scale": 4.5 }
}
}
JSON
JOB_ID=$(curl -s -X POST http://localhost:8745/v1/voice/jobs \
-H "Content-Type: application/json" -H "X-API-Key: $KEY" \
--data-binary @body.json | jq -r .job_id)
curl -s "http://localhost:8745/v1/voice/jobs/$JOB_ID/result" -H "X-API-Key: $KEY" \
| jq -r .audio_wav_base64 | base64 --decode > out.wav
Custom voice from external file (mounted WAV):
# 1) Place your consented voice sample WAV on the host (3–10 seconds is ideal)
# Requirements: mono PCM, 16‑bit, 16k or 24k sample rate recommended.
# 2) Copy or move it into the mounted voices folder (host side)
cp ~/Downloads/my-voice.wav /mnt/vv-voices/
# 3) List voices (the filename without .wav becomes the voice name)
curl -s http://localhost:8745/v1/voice/voices -H "X-API-Key: $KEY" | jq
# 4) Preview the custom voice (replace MyVoice with your filename stem)
curl -s "http://localhost:8745/v1/voice/voices/MyVoice/preview?text=Hello%20from%20my%20custom%20voice" \
-H "X-API-Key: $KEY" --output custom-preview.wav
# 5) Use it in a job
cat > job-custom.json <<'JSON'
{
"model": "vibevoice-1.5b",
"script": "Speaker 1: This is my custom narrator.",
"speakers": [ { "voiceName": "MyVoice" } ],
"overrides": { "guidance": { "inference_steps": 24, "cfg_scale": 4.2 } }
}
JSON
JOB_ID=$(curl -s -X POST http://localhost:8745/v1/voice/jobs \
-H "Content-Type: application/json" -H "X-API-Key: $KEY" \
--data-binary @job-custom.json | jq -r .job_id)
curl -s "http://localhost:8745/v1/voice/jobs/$JOB_ID/result" -H "X-API-Key: $KEY" \
| jq -r .audio_wav_base64 | base64 --decode > custom-out.wav
Tiny single‑speaker sample:
cat > tiny.json <<'JSON'
{
"model": "vibevoice-1.5b",
"script": "Speaker 1: Hello from VibeVoice.",
"speakers": [ { "voiceName": "Alice" } ],
"overrides": { "guidance": { "inference_steps": 20, "cfg_scale": 4.0 } }
}
JSON
JOB_ID=$(curl -s -X POST http://localhost:8745/v1/voice/jobs \
-H "Content-Type: application/json" -H "X-API-Key: $KEY" \
--data-binary @tiny.json | jq -r .job_id)
curl -s "http://localhost:8745/v1/voice/jobs/$JOB_ID/result" -H "X-API-Key: $KEY" \
| jq -r .audio_wav_base64 | base64 --decode > tiny.wav
Tiny two‑speaker sample:
cat > tiny2.json <<'JSON'
{
"model": "vibevoice-1.5b",
"script": "Speaker 1: Good morning!\nSpeaker 2: Morning—ready to start?",
"speakers": [ { "voiceName": "Mary" }, { "voiceName": "Frank" } ],
"overrides": { "guidance": { "inference_steps": 22, "cfg_scale": 4.2 } }
}
JSON
JOB_ID=$(curl -s -X POST http://localhost:8745/v1/voice/jobs \
-H "Content-Type: application/json" -H "X-API-Key: $KEY" \
--data-binary @tiny2.json | jq -r .job_id)
curl -s "http://localhost:8745/v1/voice/jobs/$JOB_ID/result" -H "X-API-Key: $KEY" \
| jq -r .audio_wav_base64 | base64 --decode > tiny2.wav
Four‑speaker English (~1–2 minutes):
cat > roundtable_en.json <<'JSON'
{
"model": "vibevoice-1.5b",
"script": "Speaker 1: Welcome everyone to our quick roundtable.\nSpeaker 2: Thanks—let's share highlights from this week.\nSpeaker 3: I experimented with new prompt flows and got cleaner outputs.\nSpeaker 4: Nice. I focused on latency and shaved a few seconds off.\nSpeaker 1: Did the new sampler help with stability?\nSpeaker 2: Yes, especially around tricky words and punctuation.\nSpeaker 3: I also tried a different CFG scale for warmer tone.\nSpeaker 4: And fewer inference steps were fine for short clips.\nSpeaker 1: What about longer narrations?\nSpeaker 2: We should raise steps slightly to keep quality.\nSpeaker 3: Agreed—thirty two felt like a sweet spot.\nSpeaker 4: We could A/B test with audience feedback.\nSpeaker 1: Let's line up a small user study next week.\nSpeaker 2: I can prepare scripts and scenarios.\nSpeaker 3: I will track timing and perceived naturalness.\nSpeaker 4: I'll measure CPU and GPU utilization.\nSpeaker 1: Great. Any blockers we should address?\nSpeaker 2: None on my side.\nSpeaker 3: All good here.\nSpeaker 4: Same—excited to ship improvements.\nSpeaker 1: Perfect—thanks everyone for the quick sync.",
"speakers": [
{ "voiceName": "Alice" },
{ "voiceName": "Carter" },
{ "voiceName": "Frank" },
{ "voiceName": "Mary" }
],
"overrides": { "guidance": { "inference_steps": 28, "cfg_scale": 4.5 } }
}
JSON
JOB_ID=$(curl -s -X POST http://localhost:8745/v1/voice/jobs \
-H "Content-Type: application/json" -H "X-API-Key: $KEY" \
--data-binary @roundtable_en.json | jq -r .job_id)
curl -s "http://localhost:8745/v1/voice/jobs/$JOB_ID/result" -H "X-API-Key: $KEY" \
| jq -r .audio_wav_base64 | base64 --decode > roundtable_en.wav
English + Chinese (~1–2 minutes):
cat > bilingual.json <<'JSON'
{
"model": "vibevoice-1.5b",
"script": "Speaker 1: Hello and welcome to our bilingual demo.\nSpeaker 2: 大家好,欢迎来到我们的双语演示。\nSpeaker 1: We'll alternate between English and Chinese to compare styles.\nSpeaker 2: 我会用较自然的语气来朗读示例句子。\nSpeaker 1: First, a short introduction to the topic.\nSpeaker 2: 接下来,我们会用简短的段落来说明要点。\nSpeaker 1: Pay attention to clarity and pacing across languages.\nSpeaker 2: 请留意语速与停顿是否听起来自然。\nSpeaker 1: Finally, we'll wrap up with a brief summary.\nSpeaker 2: 最后,我们会做一个简短的总结。\nSpeaker 1: Thanks for listening.\nSpeaker 2: 谢谢收听。",
"speakers": [ { "voiceName": "Alice" }, { "voiceName": "Xinran" } ],
"overrides": { "guidance": { "inference_steps": 28, "cfg_scale": 4.5 } }
}
JSON
JOB_ID=$(curl -s -X POST http://localhost:8745/v1/voice/jobs \
-H "Content-Type: application/json" -H "X-API-Key: $KEY" \
--data-binary @bilingual.json | jq -r .job_id)
curl -s "http://localhost:8745/v1/voice/jobs/$JOB_ID/result" -H "X-API-Key: $KEY" \
| jq -r .audio_wav_base64 | base64 --decode > bilingual.wav
Note on Large model:
"model": "vibevoice-large" and consider "inference_steps": 40 for best quality.The image includes demo voices under /app/voices for quick testing. Friendly names:
Use any of the above in voiceName or with the preview endpoint (e.g., /v1/voice/voices/Frank/preview).
.wav files into the mounted voices directory on the host. The container scans /app/voices and exposes each file by its filename (without extension) as a voiceName./mnt/vv-voices → container path /app/voices (see run commands above).GET /v1/voice/voices to confirm your custom voice name, then preview via GET /v1/voice/voices/{voiceName}/preview and reference it in jobs (speakers[].voiceName).Note: Advanced users may directly supply absolute voice_sample_paths to the upstream API inside the container. The public proxy focuses on folder‑based voice management via /app/voices for simplicity and safety.
AUTH_REQUIRED, API_KEY, RESET_API_KEYENABLE_1_5B, ENABLE_LARGECORS_ENABLED, ALLOWED_ORIGINSHF_HOME, HF_HUB_ENABLE_HF_TRANSFER, HUGGING_FACE_HUB_TOKENVIBEVOICE_ATTN_IMPL (default sdpa per image env; app falls back to flash_attention_2 only if the env var is unset)TLS_MODE, TLS_DOMAIN, TLS_EMAIL/root/.cache/huggingface → model cache (mount to persist downloads)/var/lib/eworker → state (API key, job state)/app/voices → voices folder (.wav files)The unified image optionally bakes model weights into the Hugging Face cache inside the image for faster cold starts.
/root/.cache/huggingface/hub/root/.cache/huggingface/hub/models--microsoft--VibeVoice-1.5B/root/.cache/huggingface/hub/models--microsoft--VibeVoice-LargeVerify inside the image:
docker run --rm eworkerinc/vibevoice:latest bash -lc \
'ls -1 /root/.cache/huggingface/hub | grep models--microsoft--VibeVoice'
Note: Baked weights reduce first-run downloads but do not change VRAM requirements; enabling both models still sums VRAM usage at runtime (~30–36 GB combined).
inference_steps (20–40 typical): higher improves quality.cfg_scale (3.5–6.0 typical): adjust style/steadiness.sample_rate_hz: 16000 or 24000.inference_steps/cfg_scale.http://localhost:8745X-API-Key: <printed-on-first-run>Content type
Image
Digest
sha256:13a444c71…
Size
34.5 GB
Last updated
about 1 year ago
docker pull eworkerinc/vibevoice