# How to Configure External Addresses for NAT Traversal in iroh

> Learn to configure external addresses for NAT traversal in iroh. Enable seamless peer connections behind NATs using external addresses for robust networking.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-07-16

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (lines 1003-1018) exposes:

- `ep.add_external_addr(addr).await` to insert new addresses
- `ep.remove_external_addr(&addr).await` to remove previously configured addresses

Both methods forward to the internal socket implementation in [`iroh/src/socket.rs`](https://github.com/n0-computer/iroh/blob/main/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:

1. **Automatic Discovery**: On startup, iroh creates a `PortMapper` that attempts to request port mappings via UPnP or PCP. When successful, the router returns an external IPv4 address that iroh monitors via `PortMapper::watch_external_address()`, as implemented in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs) (line 93).

2. **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.

3. **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.

4. **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`](https://github.com/n0-computer/iroh/blob/main/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:

```rust
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, or `add_external_addr()` and `remove_external_addr()` for runtime changes.
- The `PortMapper` component handles automatic discovery via UPnP/PCP, monitoring address changes through `watch_external_address()`.
- Address updates trigger re-evaluation in [`iroh/src/socket.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket.rs), ensuring peers always receive current reachable addresses.
- The test suite in [`iroh/tests/patchbay/nat.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/nat.rs) validates 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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.