Overview
ICE (Interactive Connectivity Establishment) is the mechanism WebRTC uses to establish peer-to-peer connections through NATs and firewalls. Proper ICE configuration is critical for reliable connectivity across different network environments.
Most ICE configuration is done through the SettingEngine. This guide focuses specifically on ICE-related settings.
ICE Servers
ICE servers (STUN/TURN) help discover public IPs and relay traffic when direct connection fails.
Basic Configuration
package main
import " github.com/pion/webrtc/v4 "
func main () {
config := webrtc . Configuration {
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string { "stun:stun.l.google.com:19302" },
},
},
}
peerConnection , err := webrtc . NewPeerConnection ( config )
if err != nil {
panic ( err )
}
defer peerConnection . Close ()
}
From configuration.go:14-59 .
Multiple STUN Servers
config := webrtc . Configuration {
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string {
"stun:stun.l.google.com:19302" ,
"stun:stun1.l.google.com:19302" ,
"stun:stun2.l.google.com:19302" ,
},
},
},
}
TURN Server Configuration
TURN servers relay traffic when direct connection and STUN fail:
config := webrtc . Configuration {
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string { "stun:stun.l.google.com:19302" },
},
{
URLs : [] string { "turn:turn.example.com:3478" },
Username : "user" ,
Credential : "password" ,
},
},
}
From iceserver.go:16-75 .
TURN over TLS
config := webrtc . Configuration {
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string { "turns:turn.example.com:5349" },
Username : "user" ,
Credential : "password" ,
},
},
}
Multiple Protocols
config := webrtc . Configuration {
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string {
"stun:stun.example.com:3478" ,
"turn:turn.example.com:3478" ,
"turns:turn.example.com:5349" ,
},
Username : "user" ,
Credential : "password" ,
},
},
}
ICE Transport Policy
Control which ICE candidates are used:
import " github.com/pion/webrtc/v4 "
// Use all candidates (default)
config := webrtc . Configuration {
ICETransportPolicy : webrtc . ICETransportPolicyAll ,
}
// Force relay (TURN only) - ensures traffic goes through TURN
config = webrtc . Configuration {
ICETransportPolicy : webrtc . ICETransportPolicyRelay ,
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string { "turn:turn.example.com:3478" },
Username : "user" ,
Credential : "password" ,
},
},
}
Using ICETransportPolicyRelay requires a TURN server and will fail if none is available.
Network Types
Control which network types are used for ICE candidates:
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// IPv4 UDP only (default)
s . SetNetworkTypes ([] webrtc . NetworkType {
webrtc . NetworkTypeUDP4 ,
})
// IPv4 and IPv6 UDP
s . SetNetworkTypes ([] webrtc . NetworkType {
webrtc . NetworkTypeUDP4 ,
webrtc . NetworkTypeUDP6 ,
})
// TCP only
s . SetNetworkTypes ([] webrtc . NetworkType {
webrtc . NetworkTypeTCP4 ,
webrtc . NetworkTypeTCP6 ,
})
// All types
s . SetNetworkTypes ([] webrtc . NetworkType {
webrtc . NetworkTypeUDP4 ,
webrtc . NetworkTypeUDP6 ,
webrtc . NetworkTypeTCP4 ,
webrtc . NetworkTypeTCP6 ,
})
From settingengine.go:286-290 .
ICE Timeouts
Configure connection timeouts and keepalive:
import (
" time "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// Configure ICE timeouts
s . SetICETimeouts (
5 * time . Second , // disconnected timeout - no activity before disconnected
25 * time . Second , // failed timeout - no activity before failed after disconnected
2 * time . Second , // keepalive interval - how often to send keepalive if no activity
)
Timeout Details
Disconnected Timeout (default: 5s)
How long without network activity before ICE considers the connection disconnected. The connection can recover from this state.
Failed Timeout (default: 25s)
How long after disconnected before ICE considers the connection failed. Recovery is unlikely after this.
Keepalive Interval (default: 2s)
How often to send keepalive packets when no media is flowing. Prevents NAT bindings from timing out.
From settingengine.go:218-237 .
Candidate Acceptance Timeouts
import (
" time "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// How long to wait before accepting different candidate types
s . SetHostAcceptanceMinWait ( 0 * time . Millisecond ) // Host candidates
s . SetSrflxAcceptanceMinWait ( 500 * time . Millisecond ) // Server reflexive
s . SetPrflxAcceptanceMinWait ( 500 * time . Millisecond ) // Peer reflexive
s . SetRelayAcceptanceMinWait ( 2000 * time . Millisecond ) // Relay (TURN)
From settingengine.go:239-257 .
Port Range
Limit the UDP port range used for ICE:
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// Restrict ICE to ports 10000-20000
err := s . SetEphemeralUDPPortRange ( 10000 , 20000 )
if err != nil {
panic ( err )
}
Limiting the port range is useful for firewall configurations. Make sure the range is large enough for your use case.
From settingengine.go:264-279 .
Single Port (UDPMux)
Serve all WebRTC traffic on a single UDP port:
import (
" github.com/pion/ice/v4 "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// All traffic goes through port 8443
mux , err := ice . NewMultiUDPMuxFromPort ( 8443 )
if err != nil {
panic ( err )
}
s . SetICEUDPMux ( mux )
api := webrtc . NewAPI ( webrtc . WithSettingEngine ( s ))
// Create multiple PeerConnections - all use port 8443
peerConnection1 , _ := api . NewPeerConnection ( webrtc . Configuration {})
peerConnection2 , _ := api . NewPeerConnection ( webrtc . Configuration {})
See full example at examples/ice-single-port/main.go .
UDPMux is extremely useful for servers that need predictable port usage for firewall rules.
ICE-TCP
Enable ICE over TCP:
import (
" net "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// Enable TCP candidates
s . SetNetworkTypes ([] webrtc . NetworkType {
webrtc . NetworkTypeTCP4 ,
webrtc . NetworkTypeTCP6 ,
})
// Create TCP listener
tcpListener , err := net . ListenTCP ( "tcp" , & net . TCPAddr {
IP : net . IP { 0 , 0 , 0 , 0 },
Port : 8443 ,
})
if err != nil {
panic ( err )
}
// Create TCPMux with max 8 connections per listener
tcpMux := webrtc . NewICETCPMux ( nil , tcpListener , 8 )
s . SetICETCPMux ( tcpMux )
api := webrtc . NewAPI ( webrtc . WithSettingEngine ( s ))
See full example at examples/ice-tcp/main.go .
ICE-TCP is useful when UDP is blocked but is generally slower than UDP.
Network Interface Filtering
Control which network interfaces are used:
import (
" net "
" strings "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// Exclude docker and virtual interfaces
s . SetInterfaceFilter ( func ( interfaceName string ) bool {
// Return true to keep, false to exclude
if strings . HasPrefix ( interfaceName , "docker" ) {
return false
}
if strings . HasPrefix ( interfaceName , "veth" ) {
return false
}
return true
})
// Only use specific interface
s . SetInterfaceFilter ( func ( interfaceName string ) bool {
return interfaceName == "eth0"
})
From settingengine.go:292-298 .
IP Address Filtering
Filter which IP addresses are used:
import (
" net "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// Only use private IPs
s . SetIPFilter ( func ( ip net . IP ) bool {
return ip . IsPrivate ()
})
// Only use public IPs
s . SetIPFilter ( func ( ip net . IP ) bool {
return ! ip . IsPrivate ()
})
// Exclude specific subnet
s . SetIPFilter ( func ( ip net . IP ) bool {
// Exclude 10.0.0.0/8
_ , excludeNet , _ := net . ParseCIDR ( "10.0.0.0/8" )
return ! excludeNet . Contains ( ip )
})
From settingengine.go:300-306 .
NAT 1:1 Mapping
Configure external IP addresses for NAT environments:
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// AWS EC2 example - private IP 10.0.1.5, public IP 203.0.113.1
s . SetNAT1To1IPs (
[] string { "203.0.113.1" },
webrtc . ICECandidateTypeHost ,
)
SetNAT1To1IPs is deprecated. Use SetICEAddressRewriteRules for more control.
Address Rewrite Rules (Recommended)
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// Replace private IP with public IP
rules := [] webrtc . ICEAddressRewriteRule {
{
External : [] string { "203.0.113.1" },
AsCandidateType : webrtc . ICECandidateTypeHost ,
Mode : webrtc . ICEAddressRewriteReplace ,
},
}
err := s . SetICEAddressRewriteRules ( rules ... )
if err != nil {
panic ( err )
}
From settingengine.go:345-368 .
ICE Lite
Enable ICE Lite mode (for servers):
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// Enable ICE Lite (typically for servers)
s . SetLite ( true )
ICE Lite is used when the endpoint has a public IP and doesn’t need to perform full ICE. Typically used on servers.
From settingengine.go:281-284 .
mDNS Configuration
Configure mDNS for local network discovery:
import (
" github.com/pion/ice/v4 "
" github.com/pion/webrtc/v4 "
)
s := webrtc . SettingEngine {}
// Enable mDNS
s . SetICEMulticastDNSMode ( ice . MulticastDNSModeQueryAndGather )
// Disable mDNS
s . SetICEMulticastDNSMode ( ice . MulticastDNSModeDisabled )
// Only respond to mDNS queries
s . SetICEMulticastDNSMode ( ice . MulticastDNSModeQueryOnly )
// Set custom mDNS hostname
s . SetMulticastDNSHostName ( "my-peer.local" )
From settingengine.go:405-416 .
Only use custom mDNS hostnames for single PeerConnection. Multiple PeerConnections with the same hostname will cause undefined behavior.
Static ICE Credentials
Set static ICE username and password:
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// Set static credentials (for signalless WebRTC)
s . SetICECredentials ( "myUserFragment" , "myPassword" )
Static credentials are useful for signalless WebRTC where peers have pre-configured connection parameters.
From settingengine.go:418-425 .
ICE Binding Requests
Limit the number of ICE binding requests:
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// Limit to 7 binding requests per candidate
var maxRequests uint16 = 7
s . SetICEMaxBindingRequests ( maxRequests )
From settingengine.go:485-489 .
Loopback Candidates
Include loopback interface in candidates:
import " github.com/pion/webrtc/v4 "
s := webrtc . SettingEngine {}
// Enable loopback candidates (useful for VMs with public IP mapped to loopback)
s . SetIncludeLoopbackCandidate ( true )
From settingengine.go:370-374 .
Complete Example
package main
import (
" net "
" strings "
" time "
" github.com/pion/ice/v4 "
" github.com/pion/webrtc/v4 "
)
func main () {
// Create SettingEngine
s := webrtc . SettingEngine {}
// Configure timeouts
s . SetICETimeouts (
5 * time . Second ,
25 * time . Second ,
2 * time . Second ,
)
// Configure port range
s . SetEphemeralUDPPortRange ( 10000 , 20000 )
// Configure network types
s . SetNetworkTypes ([] webrtc . NetworkType {
webrtc . NetworkTypeUDP4 ,
webrtc . NetworkTypeUDP6 ,
})
// Filter interfaces
s . SetInterfaceFilter ( func ( name string ) bool {
return ! strings . HasPrefix ( name , "docker" )
})
// Filter IPs
s . SetIPFilter ( func ( ip net . IP ) bool {
return ip . IsPrivate ()
})
// Configure NAT mapping
rules := [] webrtc . ICEAddressRewriteRule {
{
External : [] string { "203.0.113.1" },
AsCandidateType : webrtc . ICECandidateTypeHost ,
},
}
s . SetICEAddressRewriteRules ( rules ... )
// Create API
api := webrtc . NewAPI ( webrtc . WithSettingEngine ( s ))
// Create PeerConnection with ICE servers
config := webrtc . Configuration {
ICEServers : [] webrtc . ICEServer {
{
URLs : [] string { "stun:stun.l.google.com:19302" },
},
{
URLs : [] string { "turn:turn.example.com:3478" },
Username : "user" ,
Credential : "password" ,
},
},
}
peerConnection , err := api . NewPeerConnection ( config )
if err != nil {
panic ( err )
}
defer peerConnection . Close ()
}
Debugging ICE Issues
Enable ICE Logging
Use custom logging to see ICE candidate gathering and connectivity checks. s := webrtc . SettingEngine {
LoggerFactory : logging . NewDefaultLoggerFactory (),
}
Monitor Connection States
Track ICE connection state changes: peerConnection . OnICEConnectionStateChange ( func ( state webrtc . ICEConnectionState ) {
fmt . Printf ( "ICE State: %s \n " , state )
})
Log ICE Candidates
See what candidates are gathered: peerConnection . OnICECandidate ( func ( candidate * webrtc . ICECandidate ) {
if candidate != nil {
fmt . Printf ( "Candidate: %s \n " , candidate . String ())
}
})
Test STUN/TURN Servers
Verify your STUN/TURN servers are reachable and properly configured.
SettingEngine Complete SettingEngine reference
Custom Logging Debug ICE with custom logging