# How to Configure Relay Servers vs. Direct Connections in iroh

> Configure relay servers or direct connections in iroh. Learn to manage relay modes and force direct IP transports for optimal performance.

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

---

**Use the `relay_mode` method on the `Endpoint` builder to select production, staging, custom, or disabled relay servers, while direct IP connections are automatically attempted first and can be forced via `clear_ip_transports()` or `clear_relay_transports()`.**

The iroh networking library (n0-computer/iroh) establishes peer-to-peer connections using two distinct transport mechanisms: direct IP paths and relay-mediated tunnels. Understanding how to configure relay servers versus direct connections in iroh allows you to optimize connectivity for environments ranging from open internet deployments to strict corporate NATs.

## Direct vs. Relay Transport Mechanisms

Iroh endpoints communicate over two transport types that operate sequentially during connection establishment.

**Direct (IP) transports** are used when peers can reach each other via public or NAT-traversed IP addresses. The endpoint discovers these addresses through address-lookup services and attempts them first. These transports are added automatically when you bind an address or can be removed explicitly via builder methods.

**Relay transports** function as a fallback mechanism. When direct paths cannot be established, the endpoint automatically falls back to a relay server that forwards encrypted traffic between peers. This behavior is controlled through the `RelayMode` enum and configured via the endpoint builder.

## The RelayMode Enum

The core of relay configuration resides in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (lines 1912-1924). The `RelayMode` enum determines which relay servers an endpoint will use:

- **`RelayMode::Disabled`** – Disables all relay servers. Only direct IP paths are possible.
- **`RelayMode::Default`** – Uses the production relay map shipped with iroh.
- **`RelayMode::Staging`** – Uses the staging relay map for testing environments.
- **`RelayMode::Custom(RelayMap)`** – Accepts a user-provided list of relay URLs for self-hosted or private relay infrastructure.

### Defining Custom Relay Maps

A custom relay configuration requires the `RelayUrl` type defined in [`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs) (lines 7-22). This type wraps `url::Url` in an `Arc` for efficient cloning and validates URLs like `"https://my.relay.example.com."`.

To construct a custom relay map:

```rust
use iroh::{RelayMode, RelayUrl};

let relay_map = RelayMode::custom([
    "https://my.relay-1.example.com.".parse::<RelayUrl>()?,
    "https://my.relay-2.example.com.".parse::<RelayUrl>()?,
]);

```

The `RelayMode::custom` helper (lines 51-54 of [`endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/endpoint.rs)) creates a `RelayMap` from any iterator of `RelayUrl` values.

## Builder Methods for Transport Control

The `Endpoint` builder in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) provides specific methods to fine-tune transport behavior:

- **`relay_mode(RelayMode)`** (lines 557-575) – Inserts or replaces the relay transport configuration according to the supplied mode.
- **`clear_relay_transports()`** (lines 1099-1103) – Removes all relay transports from the builder, creating a direct-only endpoint.
- **`clear_ip_transports()`** (lines 500-507) – Removes all IP transports, forcing the endpoint to use only relay connections.

## Connection Preference and Fallback Logic

When an endpoint attempts to connect to a peer, it first tries all known **direct** addresses via IP transports. If none succeed within the timeout window, the endpoint automatically falls back to configured **relay** transports. This logic is implemented in [`iroh/src/socket/remote_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map.rs), where the `has_been_direct` flag tracks whether a direct path has been successfully established.

You do not need to manually enable direct connections—they are always attempted first. You only need to adjust configuration when you want to:

- **Force relay use** for testing behind strict NATs by calling `clear_ip_transports()`.
- **Disable relays entirely** by calling `clear_relay_transports()` or using `RelayMode::Disabled`.
- **Use private relay servers** by passing a custom `RelayMap` via `relay_mode(RelayMode::Custom(...))`.

## Configuration Examples

### Using Default Production Relays

The simplest configuration uses the `presets::N0` preset, which enables both direct IP transports and the default production relay map:

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

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let endpoint = Endpoint::builder(presets::N0).bind().await?;
    Ok(())
}

```

### Configuring Custom Relay Servers

To use your own relay infrastructure while keeping direct connections as the primary path:

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

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let custom = RelayMode::custom([
        "https://relay-1.mycorp.net.".parse::<RelayUrl>()?,
        "https://relay-2.mycorp.net.".parse::<RelayUrl>()?,
    ]);

    let endpoint = Endpoint::builder(presets::N0)
        .relay_mode(custom)
        .bind()
        .await?;
    Ok(())
}

```

### Direct-Only Mode

To disable relays entirely and allow only direct connections:

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

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let endpoint = Endpoint::builder(presets::N0)
        .clear_relay_transports()
        .bind()
        .await?;
    Ok(())
}

```

### Relay-Only Mode

To force all traffic through relays (useful for testing relay infrastructure or operating in highly restricted networks):

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

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let endpoint = Endpoint::builder(presets::N0)
        .clear_ip_transports()
        .bind()
        .await?;
    Ok(())
}

```

### Environment Variable for Staging

Setting `IROH_FORCE_STAGING_RELAYS` forces the endpoint to use the staging relay map instead of production. This is implemented in the `default_relay_mode` function (lines 564-574 of [`endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/endpoint.rs)):

```bash
export IROH_FORCE_STAGING_RELAYS=1
cargo run --example connect

```

## Summary

- **Direct connections** are attempted automatically before any relay configuration takes effect.
- **RelayMode** controls which relay servers are available: `Disabled`, `Default`, `Staging`, or `Custom`.
- **Custom relays** require `RelayUrl` values and are passed via `relay_mode(RelayMode::Custom(...))`.
- **Builder methods** `clear_relay_transports()` and `clear_ip_transports()` force direct-only or relay-only operation.
- **Environment variable** `IROH_FORCE_STAGING_RELAYS` switches to staging relays without code changes.

## Frequently Asked Questions

### How does iroh decide between direct and relay connections?

According to the source code in [`iroh/src/socket/remote_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map.rs), iroh attempts all known direct IP addresses first. If no direct path succeeds within the connection timeout, the endpoint automatically falls back to configured relay servers. Once a direct path is established, the endpoint marks the connection with `has_been_direct` and may prefer that path for subsequent traffic.

### Can I run iroh without any relay servers?

Yes. Pass `RelayMode::Disabled` to `relay_mode()` or call `clear_relay_transports()` on the endpoint builder. This creates a direct-only endpoint that will fail to connect if NAT traversal cannot establish a direct path between peers.

### What is the difference between `RelayMode::Default` and `RelayMode::Staging`?

`RelayMode::Default` uses the production relay infrastructure operated by the iroh team, while `RelayMode::Staging` points to staging servers intended for testing. You can also force staging mode at runtime by setting the `IROH_FORCE_STAGING_RELAYS` environment variable, which overrides the default selection in `default_relay_mode()` (lines 564-574 of [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)).

### How do I configure iroh to use my own relay servers?

Use `RelayMode::Custom` with a collection of `RelayUrl` values. Parse your relay URLs using `"https://your.relay.com.".parse::<RelayUrl>()?` and pass them to `RelayMode::custom()`. This method is defined at lines 51-54 of [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) and creates a `RelayMap` that the endpoint will use for fallback connections when direct paths fail.