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-definedRelayMapcontaining 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
EndpointBuilderAPI iniroh/src/endpoint.rs. - Use
RelayMode::Defaultfor production,RelayMode::Stagingfor testing, andRelayMode::Disabledfor direct-only connections. - Custom relays require a
RelayMapfromiroh-base/src/relay_url.rspassed toRelayMode::Custom. - The
IROH_FORCE_STAGING_RELAYSenvironment 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →