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.
cd deploy
cp .env.example .env # set GOOGLE_API_KEY + OwlLayer keys
docker compose up --buildLeave all LIVEKIT_* variables empty. The server boots normally; only the /owllayer/livekit/token endpoint stays inactive.
| Service | URL |
|---|---|
| OwlLayer Server WebSocket | ws://localhost:3001/owllayer |
| OwlLayer Server dashboard | http://localhost:3001/owllayer-ui |
Mode B — OwlLayer Server + LiveKit (voice rooms)
Adds a self-hosted LiveKit server via a Compose profile.
cd deploy
cp .env.example .env # also set LIVEKIT_* and LIVEKIT_URL=ws://livekit:7880
docker compose --profile livekit up --buildLIVEKIT_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 pointLIVEKIT_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
MongoStoreadapter 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:
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:
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:
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:
- The server rejects the handshake during the
HANDSHAKE_INITnegotiation. - The server sends a
SYSTEM_EVENTframe containing an error payload:"Concurrency limit exceeded for this API key". - The server immediately closes the connection, saving compute resources.
