# How Iroh Handles NAT Traversal: Hole-Punching and Relay Architecture

> Discover how Iroh achieves NAT traversal using UDP hole-punching and relay architecture. Connect peers seamlessly without manual setup.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: architecture
- Published: 2026-07-15

---

**Iroh automatically establishes peer-to-peer connections across NAT boundaries using a combination of address discovery, UDP hole-punching, and relay fallback, all orchestrated by the RemoteState actor without requiring manual configuration.**

Iroh is an open-source distributed systems toolkit built by n0-computer that prioritizes direct connectivity. Understanding how Iroh handles NAT traversal is essential for developers building decentralized applications that need to work across home routers, corporate firewalls, and mobile networks.

## The Three-Stage NAT Traversal Process

Iroh’s networking stack follows a systematic approach to bypass Network Address Translation (NAT) barriers:

1. **Address Discovery** – Each endpoint discovers and advertises local and public (STUN-discovered) addresses.
2. **Direct Path Attempt** – The system attempts UDP hole-punching to establish a direct QUIC connection between peers.
3. **Relay Fallback** – If direct connectivity fails, traffic transparently routes through iroh-relay servers.

## Managing Candidate Addresses in RemoteState

The core orchestration logic resides in [[`iroh/src/socket/remote_map/remote_state.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state.rs). This file maintains the `RemoteState` struct, which tracks potential connection paths for each remote peer:

```rust
/// Information about the last holepunching attempt.
last_holepunch: Option<HolepunchAttempt>,
/// When the next holepunch should be tried.
scheduled_holepunch: Option<Instant>,

```

When the endpoint learns new addresses—whether through local interface detection, STUN discovery, or relay advertisements—the state machine evaluates whether to initiate a new hole-punch attempt. The `candidates_changed` flag signals when the available paths have updated, triggering re-evaluation of the connection strategy.

## Triggering Hole-Punching Attempts

The `trigger_holepunching` method in [[`remote_state.rs`](https://github.com/n0-computer/iroh/blob/main/remote_state.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state.rs) serves as the decision engine for direct connectivity attempts. It implements several guard clauses to prevent unnecessary network traffic:

```rust
fn trigger_holepunching(&mut self) {
    // … several early‑exit checks …
    if let Some(ref last_hp) = self.state.last_holepunch {
        // Avoid hammering the remote if we already succeeded recently.
        if !self.candidates_changed && last_hp.succeeded_recently() {
            return;
        }
    }
    // Choose the most promising candidate pair and start a hole‑punch.
    self.initiate_holepunch(candidate_pair);
}

```

This method activates under three conditions:
- Local network addresses change (new local interfaces or ports)
- New remote candidates are discovered via STUN or relay
- A scheduled retry timer fires, ensuring persistent attempts to establish direct paths

## Path State Management and Pruning

Not all candidates succeed. Iroh tracks the success and failure history of each potential path in [[`iroh/src/socket/remote_map/remote_state/path_state.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state/path_state.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state/path_state.rs). This module implements exponential back-off and pruning logic to avoid repeatedly attempting failed paths:

- Failed hole-punch attempts are recorded with timestamps
- Back-off intervals increase exponentially to reduce network load
- Dead candidates are pruned from the active set after repeated failures

This state management ensures that the system efficiently allocates resources to viable paths while quickly abandoning routes blocked by symmetric NATs or firewall rules.

## Relay Fallback Architecture

When hole-punching fails—particularly when both peers sit behind symmetric NATs—Iroh transparently falls back to relay transport. The implementation in [[`iroh/src/socket/transports/relay.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay.rs) provides TURN-style packet forwarding:

- Relay URLs are obtained from [[`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs)](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs) and injected into the candidate list
- The fallback occurs seamlessly without application-level intervention
- Traffic flows through iroh-relay servers only when direct paths are impossible

This hybrid approach ensures connectivity in 100% of scenarios while preferring direct paths for optimal latency and throughput.

## Validating NAT Traversal: The Test Harness

The robustness of Iroh's NAT traversal is verified in [[`iroh/tests/patchbay/nat.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/nat.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/nat.rs). This comprehensive test matrix simulates various network topologies:

- **No NAT** – Direct public IP connectivity
- **Home NAT** – Standard consumer router with port mapping
- **Corporate NAT** – Symmetric NAT with strict filtering

The test harness validates that `RemoteState` correctly establishes direct connections when possible and falls back to relays when necessary.

## Code Example: Automatic NAT Traversal in Practice

Iroh's NAT traversal requires zero configuration. The following Rust example demonstrates how the client automatically handles complex NAT scenarios:

```rust
use iroh::client::Client;
use iroh::endpoint::{self, Endpoint};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create a client with the default configuration.
    // The client automatically starts the networking stack,
    // which includes NAT discovery, hole‑punching, and relay fallback.
    let client = Client::new_default().await?;

    // Connect to a remote peer (identified by its public key).
    // No extra configuration is required – the underlying
    // endpoint will perform NAT traversal as needed.
    let remote_key = "<remote‑public‑key>";
    let mut conn = client.connect(remote_key).await?;

    // Once the connection is established, we can send data.
    conn.send(b"hello from behind NAT").await?;
    Ok(())
}

```

### CLI Usage

The same automatic traversal works via command line:

```bash

# Start an iroh node (it automatically runs NAT discovery).

iroh --listen 0.0.0.0:0

# In another shell (perhaps on a different network),

# sync a directory with the first node – the library will

# hole‑punch or use the relay transparently.

iroh sync ./my_folder --remote <first‑node‑peer‑id>

```

## Summary

- **Iroh handles NAT traversal** through a three-stage process: address discovery, UDP hole-punching, and relay fallback.
- **RemoteState** in [[`remote_state.rs`](https://github.com/n0-computer/iroh/blob/main/remote_state.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state.rs) manages connection candidates and triggers hole-punch attempts based on network changes.
- **PathState** tracks candidate reliability, implementing exponential back-off and pruning for failed paths.
- **Relay transport** provides transparent fallback when direct connectivity is impossible, configured via [[`relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/relay_url.rs)](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs).
- **Zero configuration** is required—applications simply connect to peer IDs, and Iroh automatically negotiates the best available path.

## Frequently Asked Questions

### What is hole-punching in Iroh's NAT traversal?

Hole-punching is a technique where both peers simultaneously send UDP packets to each other's public addresses to create a stateful mapping in their respective NAT routers. In [[`remote_state.rs`](https://github.com/n0-computer/iroh/blob/main/remote_state.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state.rs), the `trigger_holepunching` method coordinates these simultaneous attempts, allowing direct QUIC connections even when both peers are behind NATs.

### How does Iroh decide when to use a relay server?

Iroh attempts direct hole-punching first whenever new address candidates are discovered or scheduled retries occur. Only after all candidate pairs fail—tracked in [[`path_state.rs`](https://github.com/n0-computer/iroh/blob/main/path_state.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map/remote_state/path_state.rs) with exponential back-off—does the connection transparently upgrade to relay transport. This decision happens automatically within the `RemoteState` actor without application intervention.

### Can Iroh connect through corporate firewalls and symmetric NATs?

Yes. While symmetric NATs prevent traditional hole-punching by randomizing source ports, Iroh's relay fallback in [[`transports/relay.rs`](https://github.com/n0-computer/iroh/blob/main/transports/relay.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay.rs) ensures connectivity. The test suite in [[`nat.rs`](https://github.com/n0-computer/iroh/blob/main/nat.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/nat.rs) specifically validates behavior against corporate-grade NAT configurations, ensuring reliable operation in restrictive network environments.

### Do I need to configure STUN servers or relay URLs manually?

No. Iroh ships with default relay URLs defined in [[`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs)](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs) and automatically discovers public addresses using built-in STUN infrastructure. Advanced users can override these settings, but the default configuration provides out-of-the-box NAT traversal for most deployments.