Set ICE servers either in the Janus server config or per-client.
Janus's WebRTC settings live in conf/janus.jcfg. Add a turn_* block under the nat section. Janus uses these to issue ICE candidates server-side, but most teams prefer to set ICE servers client-side so each browser learns its own credentials.
nat: {
stun_server = "stun.expressturn.com"
stun_port = 3478
full_trickle = true
turn_server = "relay1.expressturn.com"
turn_port = 3478
turn_type = "udp"
turn_user = "YOUR_EXPRESSTURN_USERNAME"
turn_pwd = "YOUR_EXPRESSTURN_PASSWORD"
ice_lite = false
ice_tcp = true
}
// Pass iceServers when attaching to a Janus plugin
Janus.init({
debug: 'all',
callback: () => {
const janus = new Janus({
server: 'wss://your-janus-server/ws',
iceServers: [
{ urls: 'stun:stun.expressturn.com:3478' },
{
urls: [
'turn:relay1.expressturn.com:3478?transport=udp',
'turn:relay1.expressturn.com:3478?transport=tcp',
'turns:relay1.expressturn.com:443?transport=tcp'
],
username: 'YOUR_EXPRESSTURN_USERNAME',
credential: 'YOUR_EXPRESSTURN_PASSWORD'
}
],
success: () => { /* attach to plugin */ }
});
}
});
Some Janus plugins (VideoRoom, Streaming, AudioBridge) can take iceServers per-handle when you call janus.attach(). Useful when you want different relay configs for publishers vs subscribers.
ice_lite = true, Janus will not use TURN itself. The browser side still can.nat_1_1_mapping in janus.jcfg if Janus runs behind a 1:1 NAT (cloud VM with public IP).Related: mediasoup recipe · LiveKit recipe · TURN for streaming