# How to Configure Relay Mode in iroh: Complete Guide to Custom and Default Relays

> Learn to configure relay mode in iroh with our complete guide. Explore default, staging, disabled, and custom relay options for optimal NAT traversal.

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

---

**Configure relay mode in iroh by calling `.relay_mode()` on the `EndpointBuilder` with variants like `RelayMode::Default`, `RelayMode::Staging`, `RelayMode::Disabled`, or `RelayMode::Custom(RelayMap)` to control NAT traversal behavior.**

The iroh networking library provides a robust relay system that routes traffic through intermediary servers when direct peer-to-peer connections fail due to NAT or firewall restrictions. The `RelayMode` enum in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) defines how your application connects to these relay servers, offering built-in presets for production and staging environments as well as support for custom relay deployments. This guide demonstrates how to configure relay mode in iroh using the `EndpointBuilder` API and environment-based overrides.

## Understanding the RelayMode Enum

The `RelayMode` enum defines four distinct strategies for relay server selection, as implemented in [[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).

- **`RelayMode::Default`** – Uses the production relay fleet operated by the iroh team (number 0 relays).
- **`RelayMode::Staging`** – Connects to the staging relay fleet for testing and CI environments.
- **`RelayMode::Disabled`** – Completely disables relay functionality and hole-punching, forcing direct connections only.
- **`RelayMode::Custom(RelayMap)`** – Accepts a user-defined `RelayMap` containing specific relay URLs for private or self-hosted infrastructure.

The [`EndpointBuilder::relay_mode()`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L557) method accepts these variants to configure the endpoint's networking behavior before initialization.

## Configuring Built-in Relay Modes

### Production Relays (Default)

The default configuration automatically selects the production relay fleet. This is the recommended setting for production applications.

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

let endpoint = Endpoint::builder()
    .relay_mode(RelayMode::Default)
    .build()?;

```

When omitted, `.relay_mode()` defaults to `RelayMode::Default` via the [`default_relay_mode()`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs#L1974) helper function.

### Staging Relays

Use the staging fleet for integration testing or continuous integration pipelines where you want to avoid production infrastructure.

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

let endpoint = Endpoint::builder()
    .relay_mode(RelayMode::Staging)
    .build()?;

```

You can also force staging mode globally by setting the `IROH_FORCE_STAGING_RELAYS` environment variable, which the `default_relay_mode()` function checks during initialization.

### Disabling Relays

For scenarios requiring strict direct connections or local testing without internet relays, explicitly disable relay functionality.

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

let endpoint = Endpoint::builder()
    .relay_mode(RelayMode::Disabled)
    .build()?;

```

This prevents the endpoint from attempting hole-punching or relay connections entirely.

## Setting Up Custom Relay Servers

When deploying private relay infrastructure, use `RelayMode::Custom` with a `RelayMap`. The `RelayMap` struct, 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), maps `RelayUrl` instances to their configurations.

Create a custom relay configuration by parsing a URL and constructing the map:

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

let relay_url: RelayUrl = "https://relay.company.internal"
    .parse()
    .expect("valid relay URL");

let relay_map = RelayMap::from(relay_url);

let endpoint = Endpoint::builder()
    .relay_mode(RelayMode::Custom(relay_map))
    .build()?;

```

The custom relay server must implement the iroh relay protocol, as found in [[`iroh-relay/src/server.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server.rs)](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/server.rs). Ensure DNS resolution works for the provided URL, or configure explicit host entries as demonstrated in the test utilities at [[`iroh/tests/patchbay/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/util.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/util.rs#L45-L62).

## Environment-Based Configuration

For applications that need runtime relay selection, inspect environment variables before building the endpoint. The [[`iroh/examples/transfer.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/transfer.rs)](https://github.com/n0-computer/iroh/blob/main/iroh/examples/transfer.rs) demonstrates this pattern:

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

fn create_endpoint() -> anyhow::Result<Endpoint> {
    let builder = Endpoint::builder();
    
    let builder = match std::env::var("IROH_ENV").as_deref() {
        Ok("dev") => {
            let url: RelayUrl = "https://dev-relay.local"
                .parse()
                .unwrap();
            builder.relay_mode(RelayMode::Custom(RelayMap::from(url)))
        }
        Ok("staging") => builder.relay_mode(RelayMode::Staging),
        _ => builder.relay_mode(RelayMode::Default),
    };
    
    builder.build()
}

```

This approach allows the same binary to use development, staging, or production relays without code changes.

## Practical Code Examples

### Complete Custom Relay Setup

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

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Configure a specific relay server
    let relay_url: RelayUrl = "https://my-relay.example.com:443"
        .parse()
        .expect("URL must be valid");
    
    let relay_map = RelayMap::from(relay_url);
    
    let endpoint = Endpoint::builder()
        .relay_mode(RelayMode::Custom(relay_map))
        .bind()
        .await?;
    
    println!("Endpoint running with custom relay: {}", endpoint.node_id());
    Ok(())
}

```

### Testing with Disabled Relays

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

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Force direct connections only
    let endpoint = Endpoint::builder()
        .relay_mode(RelayMode::Disabled)
        .bind()
        .await?;
    
    // This will only work if peers are directly reachable
    Ok(())
}

```

## Summary

- **RelayMode** controls how iroh handles NAT traversal via the `EndpointBuilder` API in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).
- Use **`RelayMode::Default`** for production, **`RelayMode::Staging`** for testing, and **`RelayMode::Disabled`** for direct-only connections.
- **Custom relays** require a `RelayMap` from [`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs) passed to `RelayMode::Custom`.
- The **`IROH_FORCE_STAGING_RELAYS`** environment variable overrides defaults globally.
- Always ensure custom relay URLs are valid and reachable before constructing the endpoint.

## Frequently Asked Questions

### What is the default relay mode in iroh?

The default relay mode is `RelayMode::Default`, which connects to the production relay fleet operated by the iroh team. This is selected automatically when you call `Endpoint::builder()` without specifying a relay mode, as determined by the `default_relay_mode()` function in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).

### How do I disable relay functionality entirely?

Set `RelayMode::Disabled` on the endpoint builder: `Endpoint::builder().relay_mode(RelayMode::Disabled)`. This prevents the endpoint from using any relay servers or attempting hole-punching, forcing direct peer-to-peer connections only.

### Can I use multiple custom relay URLs?

Yes, the `RelayMap` struct can contain multiple relay URLs. While the basic `RelayMap::from(url)` constructor creates a map with a single entry, you can construct a `RelayMap` with multiple entries to provide fallback relays or distribute load across your infrastructure.

### How does the staging relay mode differ from production?

`RelayMode::Staging` connects to the staging relay fleet used for testing and CI, while `RelayMode::Default` connects to the production fleet. The staging servers may have different stability characteristics and should not be used for production workloads. Set `IROH_FORCE_STAGING_RELAYS` to force staging mode without code changes.