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

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 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).

  • 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() 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.

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() helper function.

Staging Relays

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

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.

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), maps RelayUrl instances to their configurations.

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

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). 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#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) demonstrates this pattern:

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

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

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.
  • 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 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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →