How to Configure External Addresses for NAT Traversal in iroh
iroh supports both automatic port-mapping (UPnP/PCP) and manual configuration of external addresses to enable peers behind NATs to connect, using the Endpoint::builder().external_addr() method at startup or add_external_addr() at runtime.
The iroh networking library provides robust NAT traversal capabilities by allowing applications to specify which external addresses peers should use to reach them. Whether you rely on automatic discovery via UPnP/PCP or need to manually configure public addresses for restrictive network environments, iroh's Endpoint API gives you fine-grained control over address advertisement. This guide explains how to configure external addresses in iroh based on the actual implementation in the n0-computer/iroh repository.
Understanding External Addresses in iroh
In iroh, external addresses serve as the public-facing contact points that allow peers to establish connections when nodes reside behind NATs. The system maintains these addresses in the endpoint's internal socket and advertises them to remote peers during the connection handshake. When the set of configured external addresses changes—whether through manual updates or automatic port-mapping events—the endpoint triggers a re-evaluation via watch_external_address() to ensure peers receive the current reachable addresses.
Configuration Methods
At Build Time
The Endpoint::external_addr builder method allows you to specify addresses before the endpoint starts. According to the source code in iroh/src/endpoint.rs (lines 644-652), calling Endpoint::builder().external_addr(addr) adds the address to the internal list that gets advertised immediately upon binding.
At Runtime
For dynamic network environments, iroh provides runtime methods to modify external addresses after the endpoint is active. The implementation in iroh/src/endpoint.rs (lines 1003-1018) exposes:
ep.add_external_addr(addr).awaitto insert new addressesep.remove_external_addr(&addr).awaitto remove previously configured addresses
Both methods forward to the internal socket implementation in iroh/src/socket.rs (lines 1258-1272), which stores addresses in a Vec<SocketAddr> and triggers address re-evaluation.
How NAT Traversal Works
The NAT traversal process in iroh follows a multi-step flow that combines automatic discovery with manual overrides:
-
Automatic Discovery: On startup, iroh creates a
PortMapperthat attempts to request port mappings via UPnP or PCP. When successful, the router returns an external IPv4 address that iroh monitors viaPortMapper::watch_external_address(), as implemented iniroh/src/portmapper.rs(line 93). -
Address Advertising: Discovered or manually configured addresses are added to the endpoint's direct address list and transmitted to remote peers during the connection handshake.
-
Manual Override: For networks where automatic mapping fails—such as those with symmetric NATs or corporate firewalls—applications can supply public-reachable addresses explicitly via the builder or runtime methods.
-
Dynamic Re-evaluation: Adding or removing an address, or detecting a change from the port-mapper, triggers
watch_external_address()in the socket layer, causing the endpoint to recompute available addresses and update peers accordingly.
The system tracks these updates via the portmap_external_address_updated metric defined in iroh/src/net_report/metrics.rs (lines 15-16), providing visibility into address changes.
Code Examples
Here is how to configure external addresses in practice:
use iroh::Endpoint;
use std::net::SocketAddr;
// Configure an external address at build time
let ep = Endpoint::builder()
.external_addr("203.0.113.42:4000".parse::<SocketAddr>()?)
.bind()
.await?;
// Add an external address at runtime
let runtime_addr: SocketAddr = "198.51.100.17:5000".parse()?;
ep.add_external_addr(runtime_addr).await;
// Remove an address when it is no longer available
let removed = ep.remove_external_addr(&runtime_addr).await;
assert!(removed, "address should have been present and removed");
If you want the endpoint to rely solely on automatic port-mapping without manual configuration, omit the explicit external_addr calls. The port-mapper will discover and advertise the external address automatically when the router supports UPnP or PCP.
Summary
- iroh stores external addresses in the endpoint's internal socket and advertises them to peers during connection establishment.
- Use
Endpoint::builder().external_addr()to configure addresses before startup, oradd_external_addr()andremove_external_addr()for runtime changes. - The
PortMappercomponent handles automatic discovery via UPnP/PCP, monitoring address changes throughwatch_external_address(). - Address updates trigger re-evaluation in
iroh/src/socket.rs, ensuring peers always receive current reachable addresses. - The test suite in
iroh/tests/patchbay/nat.rsvalidates that manually added addresses appear correctly in the endpoint's address list.
Frequently Asked Questions
What is the difference between automatic and manual external address configuration?
Automatic configuration uses UPnP or PCP protocols to request port mappings from your router, discovered via the PortMapper component. Manual configuration allows you to specify fixed public addresses using external_addr() or add_external_addr() when automatic discovery fails or when you need to advertise specific IPs, such as in cloud environments with known elastic IPs.
How does iroh handle changes to external addresses at runtime?
When you call add_external_addr() or remove_external_addr(), or when the PortMapper detects a change in the router's external mapping, the socket implementation in iroh/src/socket.rs triggers watch_external_address(). This causes the endpoint to recompute its advertised address list and notify connected peers of the changes.
Can I use both automatic port-mapping and manual external addresses simultaneously?
Yes. iroh merges addresses from both sources. The endpoint maintains a Vec<SocketAddr> containing all configured addresses, whether discovered automatically by the port-mapper or added manually via the API. This hybrid approach ensures maximum connectivity in complex network topologies.
Where can I verify that my external addresses are being advertised correctly?
You can check the portmap_external_address_updated metric in iroh/src/net_report/metrics.rs, which increments each time the external address set changes. Additionally, the integration tests in iroh/tests/patchbay/nat.rs demonstrate how to verify that manually configured addresses appear in the endpoint's address list and are correctly advertised to peers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →