How to Configure iroh Relay Servers Using RelayMode
Use Builder::relay_mode() to select RelayMode::Default, Staging, Custom, or Disabled on your iroh Endpoint to control which relay servers facilitate NAT traversal when direct QUIC paths are unavailable.
When building peer-to-peer applications with the n0-computer/iroh crate, endpoints rely on relay servers to establish connectivity across restrictive firewalls and NAT. The RelayMode enum, defined in iroh/src/endpoint.rs, provides the primary mechanism to configure iroh relay servers, determining whether your node uses production infrastructure, staging environments, custom relay maps, or operates without relay assistance entirely.
Understanding RelayMode Architecture
The RelayMode enum controls how your endpoint discovers and connects to relay infrastructure. When an Endpoint starts, it measures latency to available relays and selects the fastest as its home relay.
The RelayMode Enum Variants
As implemented in iroh/src/endpoint.rs around line 1910, the enum offers four distinct variants:
- Disabled: Removes relay transport entirely. Connections must be direct or use alternative address lookup services.
- Default: Uses the production relay map shipped with iroh (
crate::defaults::prod::default_relay_map()). - Staging: Uses the staging relay map (
crate::defaults::staging::default_relay_map()). - Custom(RelayMap): Accepts an explicit
RelayMapbuilt fromRelayUrlinstances.
Transport Integration
RelayMode implements From<RelayMode> for Option<TransportConfig> in iroh/src/endpoint.rs (lines 153-170). This conversion translates your mode selection into the underlying QUIC transport configuration. When you call Builder::relay_mode(), the builder stores the resulting TransportConfig::Relay in its transport list. Selecting RelayMode::Disabled removes the relay transport entirely, while other variants populate it with the appropriate relay URLs.
Configuring Relay Modes in Practice
The Endpoint builder consumes RelayMode via the relay_mode() method. After binding, the endpoint contacts the selected relays when Endpoint::online() is awaited.
Using Production Relays (Default)
The default configuration uses iroh's production relay fleet. This is the implicit choice if you don't specify a mode, but you can set it explicitly:
use iroh::{Endpoint, RelayMode};
use iroh::endpoint::presets;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ep = Endpoint::builder(presets::N0)
.relay_mode(RelayMode::Default)
.bind()
.await?;
ep.online().await;
println!("Connected via home relay: {:?}", ep.addr().relay_urls().next());
Ok(())
}
According to the source code in iroh/src/endpoint.rs (lines 1919-1922), RelayMode::Default resolves to the production relay map.
Switching to Staging Relays
For testing or CI environments, use the staging relay infrastructure:
use iroh::{Endpoint, RelayMode};
use iroh::endpoint::presets;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ep = Endpoint::builder(presets::N0)
.relay_mode(RelayMode::Staging)
.bind()
.await?;
ep.online().await;
println!("Using staging relay(s)");
Ok(())
}
This variant maps to crate::defaults::staging::default_relay_map() as shown in lines 1920-1923 of iroh/src/endpoint.rs.
Providing Custom Relay URLs
For private deployments, construct a custom relay map using the RelayMode::custom() helper (defined around lines 50-57):
use iroh::{Endpoint, RelayMode, RelayUrl};
use iroh::endpoint::presets;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let custom = RelayMode::custom([
"https://use1-1.relay.n0.iroh.link.".parse::<RelayUrl>()?,
"https://euw-1.relay.n0.iroh.link.".parse::<RelayUrl>()?,
]);
let ep = Endpoint::builder(presets::N0)
.relay_mode(custom)
.bind()
.await?;
ep.online().await;
println!("Custom relay map active");
Ok(())
}
Note that relay URLs should include a trailing dot for DNS stability when parsing.
Disabling Relays Entirely
To force direct connections only, disable the relay transport:
use iroh::{Endpoint, RelayMode};
use iroh::endpoint::presets;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ep = Endpoint::builder(presets::N0)
.relay_mode(RelayMode::Disabled)
.bind()
.await?;
// online() returns immediately since no relay is contacted
println!("Relay disabled; direct connections only");
Ok(())
}
As implemented in iroh/src/endpoint.rs (lines 1912-1915), this variant prevents the relay transport from being added to the endpoint's transport configuration.
Environment-Based Configuration
You can override the default relay mode without code changes using the IROH_FORCE_STAGING_RELAYS environment variable. When set to a non-empty value, the default_relay_mode() function (lines 64-73 in iroh/src/endpoint.rs) automatically returns RelayMode::Staging.
use iroh::{Endpoint, RelayMode};
fn main() {
std::env::set_var("IROH_FORCE_STAGING_RELAYS", "1");
// This now returns RelayMode::Staging regardless of build configuration
let mode = iroh::default_relay_mode();
assert!(matches!(mode, RelayMode::Staging));
}
This is particularly useful for CI pipelines or test-only deployments where you want to ensure staging infrastructure usage across deployed binaries without modifying source code.
Summary
RelayModecontrols how iroh endpoints discover and connect to relay servers for NAT traversal.- Four variants are available:
Default(production),Staging(test infrastructure),Custom(user-definedRelayMap), andDisabled(no relay). - Implementation occurs in
iroh/src/endpoint.rswhereRelayModeconverts toTransportConfigand integrates with the QUIC stack viaBuilder::relay_mode()(lines 554-577). - Configuration requires calling
relay_mode()on the endpoint builder beforebind(). - Environment override
IROH_FORCE_STAGING_RELAYSforces staging mode without code changes, returningRelayMode::Stagingfromdefault_relay_mode().
Frequently Asked Questions
What is the difference between RelayMode::Default and RelayMode::Staging?
RelayMode::Default uses the production relay fleet (default_relay_map() from the prod defaults), while RelayMode::Staging connects to the staging infrastructure used for testing. The staging environment is useful for CI pipelines and development when you want to avoid hitting production relays with test traffic.
How do I run iroh without any relay servers?
Set RelayMode::Disabled when building your endpoint. This removes the relay transport entirely from the QUIC configuration in iroh/src/endpoint.rs (lines 153-170), forcing the endpoint to rely solely on direct connections or other address discovery mechanisms.
Can I use multiple custom relay URLs in iroh?
Yes. The RelayMode::custom() helper accepts any iterable of RelayUrl values, allowing you to specify multiple relay servers. The endpoint will measure latency to all provided relays and select the fastest one as its home relay. Ensure URLs include a trailing dot for DNS stability when parsing strings into RelayUrl instances.
How does the IROH_FORCE_STAGING_RELAYS environment variable work?
When IROH_FORCE_STAGING_RELAYS is set to any non-empty value, the default_relay_mode() function in iroh/src/endpoint.rs (lines 64-73) returns RelayMode::Staging instead of RelayMode::Default. This allows operators to force staging infrastructure usage across deployed binaries without recompiling, making it ideal for containerized test environments.
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 →