Voice Stack Installation
This guide covers the Voice Stack used for realtime audio sessions, speech-to-text, and text-to-speech.
Overview
| Component | Purpose | Port | URL |
|---|---|---|---|
| LiveKit | WebRTC SFU for realtime audio/video | 7880 | livekit.ravenhelm.dev |
| Whisper | Speech-to-text (ASR) | 9000 | - |
| Piper | Text-to-speech (TTS) | 10200 | - |
| Voice Gateway (optional) | Voice UI + API | 3000 / 8000 | voice.ravenhelm.dev |
Prerequisites
Before deploying the Voice Stack:
- Prerequisites completed
- Core Stack running (Traefik, Redis)
ravenhelm_netnetwork exists
Directory Structure
mkdir -p ~/ravenhelm/services/{livekit,whisper,piper,voice-gateway}
mkdir -p ~/ravenhelm/data/{livekit,whisper,piper}
Step 1: Configure LiveKit
Create ~/ravenhelm/services/livekit/livekit.yaml:
port: 7880
bind_addresses:
- ""
rtc:
port_range_start: 50000
port_range_end: 50100
tcp_port: 7881
use_external_ip: true
node_ip: <public-ip>
stun_servers:
- stun.l.google.com:19302
- stun1.l.google.com:19302
# Optional TURN servers (recommended for clients behind symmetric NAT)
# turn_servers:
# - host: global.turn.twilio.com
# port: 443
# protocol: tls
# username: <twilio-username>
# credential: <twilio-credential>
turn:
enabled: false
redis:
address: redis:6379
room:
empty_timeout: 300
max_participants: 100
logging:
level: info
json: true
prometheus_port: 6789
Create ~/ravenhelm/services/livekit/.env:
cat > ~/ravenhelm/services/livekit/.env << 'ENV'
LIVEKIT_API_KEY=<generate-secure-key>
LIVEKIT_API_SECRET=<generate-secure-secret>
REDIS_PASSWORD=<redis-password>
ENV
Step 2: Deploy LiveKit
Create ~/ravenhelm/services/livekit/docker-compose.yml:
services:
livekit:
image: livekit/livekit-server:latest
container_name: livekit
restart: unless-stopped
command: --config /etc/livekit.yaml
networks:
- ravenhelm_net
ports:
# WebRTC TCP fallback
- "7881:7881"
# WebRTC UDP media ports
- "50000-50100:50000-50100/udp"
# TURN server ports (UDP and TCP)
- "3478:3478/udp"
- "3478:3478/tcp"
# TURN relay ports
- "49152-49200:49152-49200/udp"
environment:
TZ: America/Chicago
LIVEKIT_KEYS: "${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
REDIS_PASSWORD: ${REDIS_PASSWORD}
env_file:
- .env
volumes:
- ./livekit.yaml:/etc/livekit.yaml:ro
- ../../data/livekit:/data
labels:
- "com.ravenhelm.service=livekit"
- "traefik.enable=true"
- "traefik.http.routers.livekit.rule=Host(`livekit.ravenhelm.dev`)"
- "traefik.http.routers.livekit.entrypoints=websecure"
- "traefik.http.routers.livekit.tls.certresolver=letsencrypt"
- "traefik.http.routers.livekit.service=livekit"
- "traefik.http.services.livekit.loadbalancer.server.port=7880"
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:7880"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
ravenhelm_net:
external: true
Start LiveKit:
cd ~/ravenhelm/services/livekit
docker compose up -d
Step 3: Deploy Whisper (ASR)
Create ~/ravenhelm/services/whisper/docker-compose.yml:
services:
whisper:
image: onerahmet/openai-whisper-asr-webservice:latest
container_name: whisper
restart: unless-stopped
environment:
- ASR_MODEL=base.en
- ASR_ENGINE=faster_whisper
volumes:
- ../../data/whisper:/root/.cache/huggingface
networks:
- ravenhelm_net
# Internal only - no Traefik exposure
networks:
ravenhelm_net:
external: true
Start Whisper:
cd ~/ravenhelm/services/whisper
docker compose up -d
Step 4: Deploy Piper (TTS)
Create ~/ravenhelm/services/piper/docker-compose.yml:
services:
piper:
image: lscr.io/linuxserver/piper:latest
container_name: piper
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/Chicago
- PIPER_VOICE=en_US-amy-medium
- PIPER_LENGTH=1.0
- PIPER_NOISE=0.667
- PIPER_NOISEW=0.333
volumes:
- ../../data/piper:/config
networks:
- ravenhelm_net
# Internal only - no Traefik exposure
networks:
ravenhelm_net:
external: true
Start Piper:
cd ~/ravenhelm/services/piper
docker compose up -d
Step 5: Optional Voice Gateway
The Voice Gateway provides a UI and API for LiveKit-based voice interactions. It depends on LiveKit, Whisper, Piper, Redis, and the Norns agent.
Create ~/ravenhelm/services/voice-gateway/docker-compose.yml:
services:
# LiveKit Voice Agent - handles voice transcription and synthesis
voice-agent:
build:
context: ./agent
dockerfile: Dockerfile
container_name: voice-agent
restart: unless-stopped
environment:
# Use internal Docker network URL for agent-to-server connection
# External wss://livekit.ravenhelm.dev is only for browser clients
- LIVEKIT_URL=ws://livekit:7880
- LIVEKIT_API_KEY=${LIVEKIT_API_KEY}
- LIVEKIT_API_SECRET=${LIVEKIT_API_SECRET}
- NORNS_URL=http://docs/AI-ML-Platform/norns-agent:8000
- NORNS_API_KEY=${NORNS_API_KEY}
- VOICE_USER_ID=<voice-user-id>
- DEEPGRAM_API_KEY=${DEEPGRAM_API_KEY}
- ELEVEN_API_KEY=${ELEVEN_API_KEY}
- ELEVENLABS_VOICE_ID=${ELEVENLABS_VOICE_ID}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_DB=3
- WHISPER_URL=http://whisper:9000
- PIPER_HOST=piper
- PIPER_PORT=10200
- LOG_LEVEL=INFO
networks:
- ravenhelm_net
depends_on:
- voice-api
# Settings API - manages provider configuration and LiveKit tokens
voice-api:
build:
context: ./api
dockerfile: Dockerfile
container_name: voice-api
restart: unless-stopped
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_DB=3
- LIVEKIT_API_KEY=${LIVEKIT_API_KEY}
- LIVEKIT_API_SECRET=${LIVEKIT_API_SECRET}
- WHISPER_URL=http://whisper:9000
- PIPER_HOST=piper
- PIPER_PORT=10200
- LOG_LEVEL=INFO
networks:
- ravenhelm_net
labels:
- "traefik.enable=true"
# API routes - /api/* goes to voice-api
- "traefik.http.routers.voice-api.rule=Host(`voice.ravenhelm.dev`) && PathPrefix(`/api`)"
- "traefik.http.routers.voice-api.entrypoints=websecure"
- "traefik.http.routers.voice-api.tls.certresolver=letsencrypt"
- "traefik.http.routers.voice-api.priority=20"
- "traefik.http.services.voice-api.loadbalancer.server.port=8000"
# Frontend - Next.js voice interface
voice-frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_API_URL=
- NEXT_PUBLIC_LIVEKIT_URL=wss://livekit.ravenhelm.dev
container_name: voice-frontend
restart: unless-stopped
networks:
- ravenhelm_net
labels:
- "traefik.enable=true"
# Frontend routes - everything else goes to frontend
- "traefik.http.routers.voice-frontend.rule=Host(`voice.ravenhelm.dev`)"
- "traefik.http.routers.voice-frontend.entrypoints=websecure"
- "traefik.http.routers.voice-frontend.tls.certresolver=letsencrypt"
- "traefik.http.routers.voice-frontend.priority=10"
- "traefik.http.services.voice-frontend.loadbalancer.server.port=3000"
networks:
ravenhelm_net:
external: true
Start the Voice Gateway:
cd ~/ravenhelm/services/voice-gateway
docker compose --env-file ~/ravenhelm/secrets/.env up -d
Verification
- LiveKit:
https://livekit.ravenhelm.dev - Voice Gateway UI (optional):
https://voice.ravenhelm.dev - Whisper/Piper are internal services used by voice agents