Deployment
Production deployment guide for Voicex.
Production Architecture
Environment Variables (Production)
bash
NODE_ENV=production
PORT=3001
# ─── Required ────────────────────
DEEPGRAM_API_KEY=your_key
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/voicex
JWT_SECRET=your_production_min_32_char_secret
ENCRYPTION_KEY=your_64_char_hex_key
# ─── LLM + TTS (for global providers) ────
GROQ_API_KEY=your_key
OPENAI_API_KEY=your_key
ELEVENLABS_API_KEY=your_key
# ─── Infrastructure ─────────────
REDIS_URL=redis://your-redis:6379
CORS_ORIGIN=https://your-frontend.com
# ─── Twilio (optional) ──────────
TWILIO_APP_URL=https://api.your-domain.comSee Environment Variables for the full reference.
Build & Run
Backend
bash
cd backend
pnpm install --frozen-lockfile
pnpm run build
node dist/index.jsFrontend
bash
cd frontend
pnpm install --frozen-lockfile
pnpm run build
pnpm startSeed Database (First Deploy)
bash
bash scripts/seed-plans.sh
bash scripts/seed-global-providers.sh
bash scripts/seed.shDocker
Backend Dockerfile
dockerfile
FROM node:20-slim
WORKDIR /app
COPY backend/package.json backend/pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile --prod
COPY backend/dist ./dist
EXPOSE 3001
CMD ["node", "dist/index.js"]Docker Compose (Full Stack)
yaml
version: '3.9'
services:
frontend:
build:
context: .
dockerfile: frontend/Dockerfile
ports:
- '3000:3000'
environment:
- NEXT_PUBLIC_API_URL=http://backend:3001/api
depends_on:
- backend
backend:
build:
context: .
dockerfile: backend/Dockerfile
ports:
- '3001:3001'
env_file: backend/.env.local
depends_on:
- redis
- mongo
restart: unless-stopped
redis:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
- redis_data:/data
restart: unless-stopped
mongo:
image: mongo:7
ports:
- '27017:27017'
volumes:
- mongo_data:/data/db
environment:
MONGO_INITDB_DATABASE: voicex
restart: unless-stopped
volumes:
redis_data:
mongo_data:bash
docker compose up -dScaling
Requirements for Horizontal Scaling
| Requirement | Why | Config |
|---|---|---|
| Sticky sessions | WebSocket connections must stay on the same instance | Nginx ip_hash or ALB cookie |
| Redis | Rate limits, plan cache, and conversation history shared across instances | Set REDIS_URL |
| MongoDB | All persistent data shared across instances | Set MONGODB_URI |
| Health checks | Load balancer needs to detect unhealthy instances | GET /api/health |
Nginx Configuration
nginx
upstream voicex_backend {
ip_hash;
server backend1:3001;
server backend2:3001;
server backend3:3001;
}
server {
listen 443 ssl;
server_name api.your-domain.com;
ssl_certificate /etc/ssl/certs/your-cert.pem;
ssl_certificate_key /etc/ssl/private/your-key.pem;
location / {
proxy_pass http://voicex_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400; # 24h — keep WebSocket alive
proxy_send_timeout 86400;
}
location /api/health {
proxy_pass http://voicex_backend;
proxy_read_timeout 5s;
}
}Health Check
GET /api/healthjson
{ "status": "ok", "timestamp": 1708185600000 }Use for load balancer health checks and Kubernetes liveness/readiness probes.
Monitoring
Key Metrics to Watch
| Metric | Source | Why |
|---|---|---|
| WebSocket connections | Server logs | Active voice sessions |
| Pipeline latency | Pipeline finished { elapsed } log | End-to-end turn time |
| LLM latency | LLM done { llmMs } log | Time to first token |
| TTS failures | TTS failed log | Audio generation errors |
| STT connection status | Deepgram connection opened/closed | STT health |
| Redis latency | Redis client | Cache performance |
| MongoDB query time | MongoDB profiler | DB performance |
Recommended logging stack: Pino JSON logs → Fluentd/Vector → Elasticsearch/Loki → Grafana
Production Checklist
Security
- [ ] Set
NODE_ENV=production - [ ] Set
JWT_SECRET(min 32 chars, cryptographically random) - [ ] Set
ENCRYPTION_KEY(64-char hex, cryptographically random) - [ ] Set
CORS_ORIGINto your frontend domain - [ ] SSL/TLS enabled at load balancer
- [ ] API keys use database-backed keys (not env
API_KEYS)
Infrastructure
- [ ] MongoDB connected (with connection pooling)
- [ ] Redis connected (for distributed operations)
- [ ] Sticky sessions enabled for WebSocket
- [ ] Health check monitoring (
GET /api/health)
Data
- [ ] Plans seeded (
bash scripts/seed-plans.sh) - [ ] Global providers seeded (
bash scripts/seed-global-providers.sh) - [ ] Provider registry seeded (
bash scripts/seed.sh) - [ ] MongoDB indexes created (automatic on startup)
Operations
- [ ] Log aggregation configured (Pino JSON logging)
- [ ] Alerts on TTS failures and high latency
- [ ] MongoDB backup strategy
- [ ] Auto-scaling based on WebSocket connection count
- [ ] Redis persistence configured (AOF or RDB)
Graceful Shutdown
The backend handles SIGTERM and SIGINT:
- Close all WebSocket connections
- Close HTTP server (stop accepting new connections)
- Close MongoDB connection
- Close Redis connections
- Exit process
Timeout: 10 seconds. If shutdown takes longer, the process is force-killed.
This ensures clean container restarts in Docker/Kubernetes environments.