Skip to main content

Networking & TURN

WebRTC enables low-latency audio/video streaming, but establishing peer connections through NAT and firewalls requires ICE (Interactive Connectivity Establishment) servers.

Why TURN Matters

Most users are behind NAT (Network Address Translation) which prevents direct peer-to-peer connections. ICE servers help establish connections:

  • STUN servers help discover your public IP address. They work for ~80% of connections.
  • TURN servers relay traffic when direct connections fail. Essential for corporate firewalls and symmetric NAT.

Without TURN, expect 10-20% of users to fail to connect - especially on corporate networks, mobile data, and restrictive ISPs.

How ICE Servers Are Delivered

LLMRTC handles ICE server distribution automatically:

  1. Server resolves ICE servers at startup (from config, Metered API, or defaults)
  2. When a client connects via WebSocket, the server sends a ready message containing the ICE servers
  3. Client uses these servers to establish the WebRTC connection
// Server sends this to client on WebSocket connect
{
"type": "ready",
"id": "session-uuid",
"protocolVersion": 1,
"iceServers": [
{ "urls": "stun:stun.metered.ca:80" },
{ "urls": "turn:global.relay.metered.ca:80", "username": "...", "credential": "..." },
{ "urls": "turn:global.relay.metered.ca:443?transport=tcp", "username": "...", "credential": "..." }
]
}

Configuration Options

Metered TURN provides a global TURN network with automatic credential management.

Environment Variables (CLI mode):

METERED_APP_NAME=your-app      # From Metered dashboard (e.g., 'myapp' for myapp.metered.live)
METERED_API_KEY=your-key # API key for fetching credentials
METERED_REGION=us_east # Optional: us_east, europe, asia, etc.

Library Mode:

import { LLMRTCServer } from '@llmrtc/llmrtc-backend';

const server = new LLMRTCServer({
providers: { llm, stt, tts },
metered: {
appName: 'your-app', // Required
apiKey: 'your-key', // Required
region: 'us_east' // Optional: prefer specific region
}
});

The server fetches fresh TURN credentials from Metered's API and includes them in every ready message. Credentials are cached and refreshed automatically.

Option 2: Custom ICE Servers

Provide your own STUN/TURN servers.

Environment Variables (CLI mode):

ICE_SERVERS='[{"urls":"stun:stun.example.com:3478"},{"urls":"turn:turn.example.com:3478","username":"user","credential":"pass"}]'

Library Mode:

const server = new LLMRTCServer({
providers: { llm, stt, tts },
iceServers: [
{ urls: 'stun:stun.example.com:3478' },
{
urls: 'turn:turn.example.com:3478',
username: 'user',
credential: 'pass'
},
{
urls: 'turn:turn.example.com:443?transport=tcp', // TCP fallback
username: 'user',
credential: 'pass'
}
]
});

Option 3: Client-Side Override

Clients can override server-provided ICE servers:

const client = new LLMRTCWebClient({
signallingUrl: 'ws://localhost:8787',
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
});

Option 4: Default (STUN Only)

If no ICE configuration is provided, LLMRTC uses Metered's free STUN server:

stun:stun.metered.ca:80

Warning: STUN-only mode will fail for ~15-20% of users behind strict NAT. Use TURN in production.

Resolution Priority

ICE servers are resolved in this order (first match wins):

PrioritySourceWhen Used
1Client iceServers configClient explicitly overrides
2Server iceServers configCustom ICE servers configured
3Metered APImetered config provided
4Default STUNNo configuration (fallback)

Firewall Requirements

For WebRTC to work, ensure these ports are accessible:

PortProtocolPurpose
3478UDP/TCPStandard STUN/TURN
5349TCP/TLSTURN over TLS
443TCPTURN over TCP (firewall bypass)
49152-65535UDPWebRTC media (dynamic range)

Tip: Always provide a TCP/TLS TURN fallback (turn:...?transport=tcp) for corporate networks that block UDP.

Debugging Connection Issues

Check ICE Server Delivery

In browser devtools, look for the ready message in WebSocket traffic:

// Console: verify ICE servers received
client.on('stateChange', (state) => {
console.log('Connection state:', state);
});

Common Issues

SymptomLikely CauseFix
Stuck in "connecting"No TURN server, strict NATAdd TURN servers
Works locally, fails remotelyMissing TURNAdd Metered or custom TURN
Works on WiFi, fails on mobileCarrier-grade NATEnsure TCP TURN fallback
Intermittent disconnectsTURN credential expiryUse Metered (auto-refresh)

Test Your Configuration

# Check if TURN server is reachable
curl -v telnet://turn.example.com:3478

# Test with Trickle ICE tool
# https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/

Production Recommendations

  1. Always use TURN - Don't rely on STUN alone
  2. Provide TCP fallback - Some networks block UDP entirely
  3. Use geographic distribution - Place TURN servers near users
  4. Monitor connection rates - Track ICE connection success/failure
  5. Consider Metered - Handles credentials, regions, and scaling automatically