# How Does Iroh Interact with Existing Networks? P2P Overlay on IPv4/IPv6

> Discover how Iroh builds a p2p overlay on existing IP networks. Learn about UDP sockets, lookup services, QUIC streams, and NAT fallback with relay servers.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: deep-dive
- Published: 2026-06-21

---

**Iroh builds a peer-to-peer overlay on top of existing IP networks by binding to local UDP sockets, publishing reachable addresses via pluggable lookup services, and negotiating encrypted QUIC streams that fall back to relay servers when direct connectivity is blocked by NATs.**

Iroh is a Rust implementation of a modern peer-to-peer networking stack that operates over standard IPv4 and IPv6 infrastructure. Rather than requiring specialized hardware or network configurations, it discovers connectivity paths through existing internet protocols while providing applications with encrypted, authenticated QUIC connections. This article examines the specific mechanisms in `n0-computer/iroh` that enable seamless interaction with existing networks, from socket binding to NAT traversal.

## The Three-Phase Network Architecture

Iroh interacts with existing networks through three distinct phases: local socket binding, address discovery, and NAT traversal with relay fallback. Each phase is implemented in specific source files and exposes configuration options through the `Endpoint` builder API.

### Local Network Binding

The interaction begins when an `Endpoint` binds to the host’s network interfaces. In [`src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint.rs), the `Builder` struct provides methods to configure socket binding before the endpoint becomes active.

You can bind specific addresses using `Builder::bind_addr` or `Builder::bind_addr_with_opts`, or rely on default "any-address" sockets (0.0.0.0:0 and [::]:0) that listen on all available interfaces. This initial binding creates the UDP sockets that underlie all QUIC communication.

```rust
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    // Use the "N0" preset – default relay map, portmapper, DNS resolver, etc.
    let ep = Endpoint::builder(presets::N0).bind().await?;
    
    println!("Our EndpointAddr: {}", ep.addr());
    ep.close().await;
    Ok(())
}

```

### Address Discovery via Lookup Services

Once bound, the endpoint must publish its addressing information so other peers can locate it. Iroh implements a pluggable address discovery system in [`src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/src/address_lookup.rs) centered on the `AddressLookup` trait.

The system supports multiple providers that can be combined:

- **PKARR** (HTTP-based signed packets) – Implemented in `iroh-dns` and `iroh-dns-server`, with resolution handled by `PkarrResolver`
- **DNS** – Traditional DNS zone queries via `DnsResolver` in [`iroh-dns/src/dns.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns/src/dns.rs)
- **In-process memory lookup** – For testing or when the application already knows the peer’s address
- **Custom providers** – Via the unstable `unstable-custom-transports` feature

When `Endpoint::connect` is called, iroh queries all configured lookup services concurrently through `AddressLookupServices::resolve` and merges the results, preferring direct IP addresses over relay URLs.

### NAT Traversal and Relay Assistance

For endpoints behind firewalls or NATs, iroh attempts to open direct paths using the portmapper module in [`src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/src/portmapper.rs). When the `portmapper` feature is enabled, `PortmapperConfig::Enabled` creates a `portmapper::Client` that automatically discovers external IPv4 addresses via UPnP, PCP, or NAT-PMP and publishes these via address-lookup services.

If direct connectivity fails, iroh falls back to **relay servers** configured via `Builder::relay_mode` and stored in a `RelayMap` (defined in [`iroh-relay/src/relay_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/relay_map.rs)). The relay performs the QUIC handshake on behalf of both peers, forwarding encrypted packets without exposing plaintext content. This enables connectivity even when both endpoints are behind symmetric NATs.

All traffic after the handshake uses TLS-based QUIC encryption ([`src/transport.rs`](https://github.com/n0-computer/iroh/blob/main/src/transport.rs) and [`src/quic.rs`](https://github.com/n0-computer/iroh/blob/main/src/quic.rs)), ensuring end-to-end confidentiality regardless of whether the path is direct or relayed.

## Connection Flow: From Bind to QUIC Stream

Understanding how iroh interacts with existing networks requires following the complete connection lifecycle:

1. **Endpoint creation** – `Endpoint::builder(presets::N0)` creates a `Builder` pre-configured with a default relay map, enabled portmapper, and DNS resolver.

2. **Socket binding** – `builder.bind().await?` binds default IPv4/IPv6 sockets. Additional sockets can be bound with `bind_addr` to restrict traffic to specific interfaces.

3. **Address publication** – The endpoint calls `address_lookup().publish(&data)` whenever external addresses change (e.g., after a successful portmapper mapping). Published data includes the external IP, relay URL, and user-defined metadata.

4. **Peer resolution** – When `Endpoint::connect(remote, alpn)` is called, iroh checks cached `EndpointAddr` for direct IPs. If none are viable, it queries all configured address-lookup services concurrently and merges results.

5. **Connection establishment** – Iroh selects the first usable address (or home relay) and initiates a QUIC connection via `noq_endpoint().connect_with`. If direct, traffic flows peer-to-peer over UDP; otherwise, it routes through the selected relay.

## Configuring Network Interaction

The following examples demonstrate how to customize iroh's interaction with existing networks using the builder API.

### Custom Relay with Disabled Portmapper

To use a specific relay server while disabling automatic NAT traversal:

```rust
use iroh::{Endpoint, endpoint::presets, RelayUrl, PortmapperConfig};

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let custom_relay: RelayUrl = "https://my-relay.example.com".parse()?;
    let ep = Endpoint::builder(presets::Minimal)
        .relay_mode(custom_relay.into())
        .portmapper_config(PortmapperConfig::Disabled)
        .bind()
        .await?;

    println!("Endpoint with custom relay: {}", ep.addr());
    Ok(())
}

```

### Manual External Address Publication

For scenarios where the external address is known in advance (e.g., in containerized environments with explicit port mappings):

```rust
use std::net::SocketAddr;
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let ep = Endpoint::builder(presets::Minimal).bind().await?;
    let external: SocketAddr = "203.0.113.42:34567".parse()?;
    ep.add_external_addr(external).await;
    Ok(())
}

```

### Monitoring Address Changes

Subscribe to address updates to detect when the portmapper successfully creates a mapping or when relay URLs change:

```rust
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let ep = Endpoint::builder(presets::N0).bind().await?;
    let mut watcher = ep.watch_addr().stream();

    while let Some(addr) = watcher.next().await {
        println!("New advertised address: {}", addr);
    }
    Ok(())
}

```

### Connecting by Endpoint ID Only

Iroh can connect to peers using only their `EndpointId` (public key), automatically resolving addresses through configured lookup services:

```rust
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let ep_a = Endpoint::builder(presets::Minimal).bind().await?;
    let ep_b = Endpoint::builder(presets::Minimal).bind().await?;

    let a_id = ep_a.id();
    let conn = ep_b.connect(a_id, b"my-alpn").await?;
    
    println!("Connected! Remote ID = {}", conn.remote_id());
    Ok(())
}

```

## Summary

- **Iroh binds to existing IP networks** through standard UDP sockets via `Builder::bind`, supporting specific interface binding or any-address defaults.
- **Address discovery is pluggable** through the `AddressLookup` trait in [`src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/src/address_lookup.rs), supporting PKARR, DNS, and custom providers.
- **NAT traversal is automatic** via the portmapper module ([`src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/src/portmapper.rs)) which attempts UPnP/PCP/NAT-PMP mappings before falling back to relay servers.
- **Relays enable universal connectivity** by forwarding encrypted QUIC packets when direct paths are blocked, configured through `RelayMap` in [`iroh-relay/src/relay_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/relay_map.rs).
- **All traffic is encrypted end-to-end** using TLS-based QUIC, ensuring that relay servers cannot inspect connection contents.

## Frequently Asked Questions

### Does iroh require public IP addresses to function?

No. Iroh functions across NATs and private networks by using relay servers as a fallback when direct connectivity fails. The `PortmapperConfig` enables automatic discovery of external addresses when UPnP or NAT-PMP is available, but iroh remains functional even when endpoints are behind symmetric NATs with no direct path, routing encrypted traffic through configured relays.

### How does iroh discover peer addresses without a central server?

Iroh uses the `AddressLookup` trait system in [`src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/src/address_lookup.rs) to query multiple sources concurrently. By default, it checks PKARR (signed DNS packets over HTTP), traditional DNS zones, and in-memory caches. Application developers can also implement custom lookup providers via the `unstable-custom-transports` feature, allowing integration with existing discovery mechanisms like DHTs or centralized registries.

### What protocols does iroh use for the underlying network transport?

Iroh uses **UDP** for the underlying transport, implementing **QUIC** (HTTP/3) for the actual connection layer. All packets are encrypted using TLS 1.3, and the protocol supports 0-RTT connection resumption for subsequent connections between known endpoints. This design ensures compatibility with existing internet infrastructure while providing modern security and performance characteristics.