In mediasoup,
ipcontrols local interface binding on the server, whileannounceAddresspublishes the reachable address to clients through ICE candidates. Mixing them up can break WebRTC connectivity, especially in NAT, containerized, and0.0.0.0scenarios. Keywords: mediasoup, WebRTC, ICE.
The technical specification snapshot clarifies the deployment context
| Parameter | Description |
|---|---|
| Core language | JavaScript / C++ |
| Key protocols | WebRTC, ICE, SDP, UDP, TCP |
| Typical components | WebRtcServer, listenInfos |
| Configuration focus | ip, announceAddress |
| Applicable scenarios | Public internet deployment, NAT, Docker/K8s, multi-NIC hosts |
| Reference popularity | Original article shows about 330 reads |
| Core dependencies | mediasoup, browser WebRTC stack, signaling channel |
ip and announceAddress serve two different layers of responsibility
ip determines which local network interface mediasoup listens on for packets. It is the binding address from the operating system network stack’s perspective. This address must actually exist on the server, or you can use 0.0.0.0 to listen on all interfaces.
announceAddress determines which external address the server writes into ICE candidates. It is the connection target seen by the client. The client does not care which internal NIC your process binds to. It only cares whether the address exposed in SDP/ICE is truly reachable.
You can remember the difference in one sentence
ip means “where I receive packets,” while announceAddress means “where you should connect.” The former is host-facing, and the latter is client-facing.
| Parameter | Responsibility layer | Typical values | Required | Consequence of misconfiguration |
|---|---|---|---|---|
ip |
Local binding layer | 127.0.0.1, 192.168.1.100, 0.0.0.0 |
Yes | The server cannot listen correctly |
announceAddress |
Client addressing layer | Public IP, domain name, load balancer address | Conditionally required | ICE candidates become unreachable and media fails |
const listenInfos = [
{
protocol: 'udp',
ip: '192.168.1.100', // The actual private address bound by the server
announceAddress: '203.0.113.10', // Tell clients to connect to the public address
port: 44444
}
];
This configuration expresses the most common deployment intent: listen on a private interface and publish a public entry point externally.
You can omit announceAddress in a direct public internet deployment
If the server binds directly to a public IP and that same address is reachable by clients, then ip already matches the external address and you can leave announceAddress unset.
This is the simplest scenario because ICE candidates can directly include the public address, and the client can complete connectivity checks using that candidate.
A single public NIC is the easiest deployment model
const listenInfos = [
{
protocol: 'udp',
ip: '203.0.113.10', // Bind directly to the public IP
port: 44444
}
];
This configuration fits environments where the server has a stable public IP and no additional address translation.
Private network deployments behind NAT or firewalls must explicitly announce an external address
When mediasoup runs on a private address, such as a cloud internal network, a datacenter NAT environment, or behind a home router, clients cannot directly access 192.168.x.x or 10.x.x.x addresses. In this case, you must use announceAddress to publish the public entry point.
If you omit this field, the server will generate ICE candidates that expose the private address. The browser will then try to connect to it directly, but public networks cannot route to private ranges, so ICE checks eventually fail.
In NAT scenarios, candidate reachability matters more than successful listening
const listenInfos = [
{
protocol: 'udp',
ip: '192.168.1.100', // Bind to the private NIC
announceAddress: '203.0.113.10', // Publish the public address behind NAT
port: 44444
}
];
This configuration only works when the gateway has already completed port mapping, for example 203.0.113.10:44444 -> 192.168.1.100:44444.
When ip is 0.0.0.0, announceAddress is effectively required
0.0.0.0 means listen on all local interfaces. It works well in multi-NIC, containerized, and complex network environments. However, this address is valid only for server-side binding and must never be exposed to clients as a connection target.
For that reason, once you set ip to 0.0.0.0, you should also provide a clear public IP, domain name, or load balancer entry point as announceAddress.
0.0.0.0 solves listening scope, not external reachability
const listenInfos = [
{
protocol: 'udp',
ip: '0.0.0.0', // Listen on all interfaces
announceAddress: 'sfu.example.com', // Expose a stable entry point to clients
port: 44444
}
];
This configuration is common in Docker, Kubernetes, dual-NIC hosts, and SFU services exposed through a domain name.
announceAddress directly affects how ICE candidates are generated
In mediasoup’s actual behavior, the candidate address does not blindly use the local bound IP. Instead, it prefers announceAddress when present. That is exactly why it directly affects the candidate lines in SDP.
If the announced address is wrong, the client will still connect to the wrong destination even if the UDP port is genuinely listening. The symptom is usually successful signaling but no audio or video.
The candidate construction logic can be abstracted as follows
function buildIceCandidate(listenInfo) {
let candidateIp = listenInfo.ip; // Use the local bound address by default
const candidatePort = listenInfo.port;
if (listenInfo.announceAddress) {
candidateIp = listenInfo.announceAddress; // If an announced address exists, publish it externally instead
}
return `candidate:1 1 ${listenInfo.protocol.toUpperCase()} 100 ${candidateIp} ${candidatePort} typ host`;
}
This pseudocode shows that the address the client ultimately tries to connect to depends primarily on the announced address, not on the listening semantics.
During troubleshooting, you should verify candidate addresses before checking only whether the port is open
Many deployment issues do not happen because mediasoup failed to start. They happen because SDP exposes the wrong address. During debugging, first inspect the remote candidate received by the browser and confirm whether the IP or domain name is the actual entry point reachable by the client.
If signaling works, DTLS never establishes, and media traffic never flows, you should usually check these three items first:
- Whether you used a private
ip. - Whether you forgot
announceAddress. - Whether the NAT port mapping matches the announced port.
The safest production configuration guidance is straightforward
- Single public host: set
ipdirectly to the public IP. - NAT or container environment: set
ipto the actual bound address andannounceAddressto the public entry point. - Multi-NIC environment: prefer
0.0.0.0with an explicitannounceAddress. - When using a domain name: ensure DNS resolves correctly and the entry layer forwards UDP/TCP properly.
The FAQ answers the most common configuration questions
1. Why can the browser join the room when ip is set to 192.168.x.x, but there is no audio or video?
Because signaling success does not mean media connectivity success. If the remote ICE candidate exposes a private address, public clients cannot reach it, ICE fails, and no media stream is established.
2. Can announceAddress be a domain name?
Yes. As long as the domain can be resolved by the client and ultimately points to a genuinely reachable public entry point, you can use it as the announced external address.
3. What configuration is recommended for Docker or Kubernetes?
In most cases, set ip: '0.0.0.0' to listen on container interfaces, then use announceAddress to publish the host public IP, load balancer address, or service domain name. This is usually the safer approach.
The references align with mediasoup ICE address announcement behavior
This article reconstructs the topic based on the original source material. Its core conclusions are consistent with mediasoup’s address announcement behavior during WebRTC ICE negotiation. The references include technical articles on mediasoup protocol interactions and deployment experience summaries.
AI Readability Summary: This article focuses on listenInfos configuration in mediasoup WebRtcServer, explains the distinct roles of ip for local binding and announceAddress for client addressing, and uses public internet, NAT, and 0.0.0.0 deployment scenarios to show common failure causes and reliable configuration patterns.