Skip to content

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.com

See Environment Variables for the full reference.


Build & Run

Backend

bash
cd backend
pnpm install --frozen-lockfile
pnpm run build
node dist/index.js

Frontend

bash
cd frontend
pnpm install --frozen-lockfile
pnpm run build
pnpm start

Seed Database (First Deploy)

bash
bash scripts/seed-plans.sh
bash scripts/seed-global-providers.sh
bash scripts/seed.sh

Docker

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 -d

Scaling

Requirements for Horizontal Scaling

RequirementWhyConfig
Sticky sessionsWebSocket connections must stay on the same instanceNginx ip_hash or ALB cookie
RedisRate limits, plan cache, and conversation history shared across instancesSet REDIS_URL
MongoDBAll persistent data shared across instancesSet MONGODB_URI
Health checksLoad balancer needs to detect unhealthy instancesGET /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/health
json
{ "status": "ok", "timestamp": 1708185600000 }

Use for load balancer health checks and Kubernetes liveness/readiness probes.


Monitoring

Key Metrics to Watch

MetricSourceWhy
WebSocket connectionsServer logsActive voice sessions
Pipeline latencyPipeline finished { elapsed } logEnd-to-end turn time
LLM latencyLLM done { llmMs } logTime to first token
TTS failuresTTS failed logAudio generation errors
STT connection statusDeepgram connection opened/closedSTT health
Redis latencyRedis clientCache performance
MongoDB query timeMongoDB profilerDB 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_ORIGIN to 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:

  1. Close all WebSocket connections
  2. Close HTTP server (stop accepting new connections)
  3. Close MongoDB connection
  4. Close Redis connections
  5. Exit process

Timeout: 10 seconds. If shutdown takes longer, the process is force-killed.

This ensures clean container restarts in Docker/Kubernetes environments.

Built with Deepgram, Groq, and ElevenLabs.