# How to Set Up an iroh Network: A Complete Guide

> Set up an iroh network by deploying a relay server configuring peers and optionally running a DNS server for easy addressing. This guide provides step-by-step instructions.

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

---

**To set up an iroh network, deploy an iroh-relay server for NAT traversal fallback, configure each peer with an Endpoint pointing to the relay, and optionally run an iroh-dns-server for human-readable addressing.**

Setting up an **iroh network** involves configuring the distributed infrastructure that enables peer-to-peer connectivity through NAT traversal and encrypted QUIC streams. The `n0-computer/iroh` repository provides the necessary binaries and libraries to establish this connectivity with minimal boilerplate. This guide walks through deploying the required relay infrastructure, initializing client endpoints, and optionally configuring DNS resolution for production deployments.

## Architecture Overview

An iroh network consists of three distinct components that work together to provide seamless peer-to-peer connectivity:

1. **iroh-relay** – A publicly accessible server that forwards encrypted packets when direct connections fail due to strict NATs or firewalls.
2. **Endpoint** – The client-side abstraction (created via the `iroh` crate) that handles key generation, hole-punching, and protocol multiplexing.
3. **iroh-dns-server** (optional) – A DNS service that resolves **PKARR** records, enabling human-readable addressing like `endpoint-id.iroh.link`.

## Deploying the iroh-relay Server

The **iroh-relay** is the only mandatory infrastructure component. It exposes an HTTP API on port 3340 for client registration and can optionally provide QUIC address discovery on port 7824.

### Development Mode

For local testing, run the relay in development mode without TLS:

```bash
cargo run --features="server" --bin iroh-relay -- --dev

```

This command compiles and starts the relay with permissive settings suitable for testing. According to the [`iroh-relay/README.md`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/README.md), the `--dev` flag disables certificate validation and enables plain HTTP communication.

### Production Configuration

For production deployments, configure access control via a TOML configuration file:

```toml

# config.toml

access = "everyone"

```

The `access` field supports allow-lists, deny-lists, or shared-token schemes for restricting which endpoint IDs can utilize the relay. Refer to the access control documentation in [`iroh-relay/README.md`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/README.md) for production-hardening options.

## Creating an Endpoint in Rust

Each participant in the network creates an **Endpoint** using the `iroh` crate. The endpoint automatically generates a permanent **EndpointId** (a cryptographic public key) and manages the connection lifecycle.

### Basic Endpoint Setup

The following example, adapted from [`iroh/examples/connect.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/connect.rs), demonstrates the minimal code required to join an iroh network:

```rust
use iroh::endpoint::Endpoint;
use iroh::relay::RelayMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create a local endpoint with a persistent EndpointId
    let endpoint = Endpoint::bind().await?;
    
    // Configure the relay for fallback connectivity
    let relay_cfg = RelayMap::default()
        .with_url("http://localhost:3340".parse()?);
    endpoint.set_relay_map(relay_cfg).await?;
    
    // Connect to a remote peer using their EndpointId
    let remote_id: iroh::EndpointId = "iroh:...".parse()?;
    let conn = endpoint
        .connect(remote_id, b"my-proto/0.1")
        .await?;
    
    // Open a bidirectional QUIC stream
    let (mut send, mut recv) = conn.open_bi().await?;
    send.write_all(b"hello iroh").await?;
    send.finish()?;
    
    let reply = recv.read_to_end(1024).await?;
    println!("reply: {}", String::from_utf8_lossy(&reply));
    
    Ok(())
}

```

### Key Methods and Files

The implementation relies on several core components defined in the source tree:

- **`Endpoint::bind()`** (defined in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs)) – Creates a UDP/QUIC socket and persists the endpoint ID to `$XDG_DATA_HOME/iroh/endpoint-id`.
- **`RelayMap::default()`** (from [`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs)) – Constructs a relay configuration supporting multiple URLs for redundancy.
- **`set_relay_map()`** – Registers the relay configuration with the endpoint, enabling fallback when direct hole-punching fails.

## Optional DNS and PKARR Resolution

While peers can connect directly using raw **EndpointId** strings, the **iroh-dns-server** provides human-readable addressing through PKARR (Public Key Addressable Resource Records).

### Running the DNS Server

Start the DNS server with PKARR support enabled:

```bash
cargo run --bin iroh-dns-server -- --config-path=example-dns.toml

```

Configuration requires enabling the PKARR service in the TOML file:

```toml
listen = "0.0.0.0:53"
pkarr = true

```

With this service running, peers resolve addresses like `endpoint-id.iroh.link` to their current QUIC sockets without exchanging raw UDP information. See [`iroh-dns-server/README.md`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/README.md) for zone configuration and forwarding rules.

### Address Resolution Types

The system supports multiple address types defined in [`iroh-base/src/endpoint_addr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/endpoint_addr.rs), including raw UDP sockets, QUIC addresses, and PKARR DNS records.

## Complete Working Example

To establish a functional iroh network:

1. **Start the relay infrastructure** using either development mode (`--dev`) or a production configuration with TLS certificates.
2. **Initialize peer endpoints** by calling `Endpoint::bind()` and configuring the `RelayMap` with your relay URL.
3. **Exchange EndpointIds** either out-of-band or via the DNS server if deployed.
4. **Establish connections** using `endpoint.connect()` with an ALPN protocol identifier (e.g., `b"my-proto/0.1"`), which enables protocol multiplexing over single connections.

The [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs) file provides a complete client-server demonstration showing bidirectional stream handling, while [`iroh/examples/transfer.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/transfer.rs) illustrates file transmission over custom protocols.

## Summary

- **Deploy iroh-relay** – The only required infrastructure component for NAT traversal fallback and connectivity between restricted peers.
- **Configure Endpoint** – Use `Endpoint::bind()` and `set_relay_map()` in Rust applications to join the network with automatic hole-punching.
- **Optional DNS** – Run iroh-dns-server to enable PKARR-based human-readable addressing (`*.iroh.link`).
- **Core Implementation** – All networking logic resides in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs), with type definitions in `iroh-base` for `RelayUrl` and endpoint addressing.

## Frequently Asked Questions

### What infrastructure is required to set up an iroh network?

The minimum requirement is a single **iroh-relay** server. This binary handles NAT traversal and packet forwarding when direct connections fail. While optional, the **iroh-dns-server** simplifies peer discovery by mapping endpoint IDs to DNS names. No central coordinator or blockchain is required for basic operation.

### Can I run an iroh network without a relay?

While possible on local networks with unrestricted UDP, the relay is essential for production deployments where peers reside behind corporate firewalls or symmetric NATs. The relay acts as a fallback path when **hole-punching** fails, ensuring connectivity in approximately 95% of network topologies.

### How does the EndpointId persist between application restarts?

The `Endpoint::bind()` method automatically generates and stores the **EndpointId** (Ed25519 public key) in the platform-specific data directory (`$XDG_DATA_HOME/iroh/endpoint-id` on Linux). Subsequent calls to `bind()` load this identity, maintaining stable addressing across process lifecycles.

### What is the difference between RelayMap and direct addressing?

**RelayMap** configures fallback servers for when direct UDP connectivity fails, while direct addressing (specifying an IP and port) attempts immediate connection. The endpoint always attempts direct hole-punching first; the relay map only activates after direct attempts timeout, ensuring optimal performance.