Skip to content

Production Deployment & Scaling

Deploying OwlLayer as a real-time WebSockets and WebRTC engine in production requires special infrastructure considerations. This guide covers Docker deployment, horizontal scaling, SSL configurations, and rate-limiting strategies.


0. Docker Deployment

The repo ships a ready-to-run stack under deploy/. LiveKit is optional: OwlLayer Server runs standalone (WebSocket AITP + text + Gemini native audio) and LiveKit is only added when you need WebRTC voice rooms.

Mode A — OwlLayer Server only (no LiveKit)

This is the default. One container, no LiveKit needed.

bash
cd deploy
cp .env.example .env      # set GOOGLE_API_KEY + OwlLayer keys
docker compose up --build

Leave all LIVEKIT_* variables empty. The server boots normally; only the /owllayer/livekit/token endpoint stays inactive.

ServiceURL
OwlLayer Server WebSocketws://localhost:3001/owllayer
OwlLayer Server dashboardhttp://localhost:3001/owllayer-ui

Mode B — OwlLayer Server + LiveKit (voice rooms)

Adds a self-hosted LiveKit server via a Compose profile.

bash
cd deploy
cp .env.example .env      # also set LIVEKIT_* and LIVEKIT_URL=ws://livekit:7880
docker compose --profile livekit up --build

LIVEKIT_API_KEY / LIVEKIT_API_SECRET must match on both sides (deploy/livekit.yaml and the OwlLayer Server environment). Secrets never reach the browser: the client fetches a short-lived room token from /owllayer/livekit/token.

The OwlLayer Server image is built from apps/demo-server/Dockerfile (multi-stage, pnpm monorepo). To use LiveKit Cloud instead of the bundled node, run Mode A and point LIVEKIT_URL / keys at your Cloud project.


1. Horizontal Scaling & Session Sticking

Because OwlLayer Server sessions hold memory state (OwlLayerAgent context buffers) and manage persistent WebSocket connections, scaling horizontally across multiple servers requires a shared state store and routing configurations:

  • MongoDB Store: Use the MongoStore adapter to persist and share session history snapshots across multiple servers.
  • Session Stickiness: Ensure your load balancer (e.g. AWS ALB, HAProxy, Cloudflare) is configured with Session Affinity (Sticky Sessions). This guarantees that WebSocket frames from a specific client are routed to the same Node.js server instance handling the active pipeline.

2. Reverse Proxy Configuration (Nginx & Caddy)

Production environments require secure connections (wss:// / HTTPS) to satisfy browser permissions for microphone access.

Nginx Configuration Example

Configure Nginx as a reverse proxy to terminate SSL and handle WebSocket upgrade headers:

nginx
server {
    listen 443 ssl;
    server_name api.owllayer.dev;

    ssl_certificate /etc/letsencrypt/live/api.owllayer.dev/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.owllayer.dev/privkey.pem;

    location /owllayer {
        proxy_pass http://localhost:4001;
        
        # Enable WebSocket upgrades
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # Forward client headers
        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_set_header X-Forwarded-Proto $scheme;

        # Disable buffers to enable real-time streaming
        proxy_buffering off;
        
        # Set timeouts suitable for long websocket calls
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Caddyfile Configuration Example

Caddy handles automatic SSL certificates out-of-the-box and manages WebSockets cleanly:

caddy
api.owllayer.dev {
    # Reverse proxy websocket traffic to Node server
    reverse_proxy /owllayer* localhost:4001 {
        header_up Host {host}
        header_up X-Real-IP {remote}
    }
}

3. Concurrency Limits & Rate-Limiting (virtualLines)

WebSocket connections are resource-intensive. To prevent server exhaustion (CPU/RAM spikes during heavy audio processing), OwlLayerServer features a rate-limiting tool called virtualLines.

You configure concurrent connection slots per API key directly in your server options:

typescript
import { OwlLayerServer } from '@owllayer/server';

const server = new OwlLayerServer({
  llm: llmAdapter,
  virtualLines: {
    // Defines lines limit specifications
    lines: [
      { 
        apiKey: 'pk_free_trial_xxx', 
        count: 5,           // Maximum 5 concurrent WebSocket connections allowed
        ttlMs: 1800000      // Connection duration cap (e.g., 30 minutes)
      },
      { 
        apiKey: 'pk_enterprise_xxx', 
        count: 100,         // Maximum 100 concurrent lines
        ttlMs: 3600000
      }
    ]
  }
});

Handshake Rejection

If a client attempts to connect using an API key that has already exhausted its concurrency slots:

  1. The server rejects the handshake during the HANDSHAKE_INIT negotiation.
  2. The server sends a SYSTEM_EVENT frame containing an error payload: "Concurrency limit exceeded for this API key".
  3. The server immediately closes the connection, saving compute resources.

Released under the MIT License.