How to Configure iroh Nodes: Complete Builder API Guide
You configure iroh nodes using the chainable Builder API exposed through iroh::endpoint::Builder, which provides methods like secret_key(), alpns(), and relay_mode() that must be chained before calling bind() to instantiate the Endpoint.
The n0-computer/iroh repository implements a peer-to-peer networking stack where node configuration happens through a type-safe builder pattern. All tunable parameters for an iroh node are defined in iroh/src/endpoint.rs, allowing you to customize cryptographic identity, transport protocols, relay connectivity, and address resolution before the node opens its sockets.
Understanding the Builder Pattern
The Builder struct in iroh/src/endpoint.rs serves as the central configuration interface. You initialize it using either Builder::empty() for a bare configuration or Builder::new(preset) with predefined defaults like iroh::endpoint::presets::N0 or N0_STAGING.
When using Builder::empty(), the node starts with relay_mode: RelayMode::Disabled and generates a fresh SecretKey automatically via Default::default(). All configuration methods return Self, enabling fluid method chaining that terminates with bind().await to create the live Endpoint.
Essential Configuration Settings
Cryptographic Identity with Secret Keys
The secret_key() method controls your node's cryptographic identity. If omitted, the builder generates a new SecretKey automatically, which is useful for ephemeral nodes but unsuitable for persistent identities.
use iroh::SecretKey;
let secret = SecretKey::generate();
builder.secret_key(secret);
Application Protocol Negotiation (ALPN)
The alpns() method is required if your node accepts incoming connections. By default, the ALPN list is empty, which prevents the node from responding to connection requests.
builder.alpns(vec![b"my-protocol/1.0".to_vec()]);
Relay Server Configuration
Use relay_mode() to specify how nodes traverse NAT and reach peers behind firewalls. The default in Builder::empty() is RelayMode::Disabled, while production deployments typically use RelayMode::Default to connect to the number0 relay infrastructure defined in iroh/src/defaults.rs.
// Production relays
builder.relay_mode(iroh::RelayMode::Default);
// Custom private relay
use iroh::RelayUrl;
let custom = vec![RelayUrl::from_str("https://relay.example.com")?];
builder.relay_mode(iroh::RelayMode::Custom(custom.into()));
TLS Trust Configuration
The ca_tls_config() method configures the root certificate store for TLS connections to relays and peers. The default uses CaTlsConfig::default() (system root certificates), but you can override this for private relays with self-signed certificates.
use iroh::tls::CaTlsConfig;
// Skip verification for private/testing relays
builder.ca_tls_config(CaTlsConfig::insecure_skip_verify());
Address Discovery Services
The address_lookup() method (line 605 in endpoint.rs) enables DNS-based or PKARR-based peer discovery. Without this, you must provide direct addressing information for every connection.
use iroh::address_lookup::DnsAddressLookupBuilder;
builder.address_lookup(DnsAddressLookupBuilder::default());
Transport and NAT Configuration
Control underlying network transports using clear_ip_transports() and add_custom_transport(). By default, the builder automatically adds IPv4 and IPv6 QUIC transports. You can remove these and inject custom transports if needed.
use iroh::transport::QuicConfig;
builder
.clear_ip_transports() // Remove auto-added IPv4/IPv6
.add_custom_transport(QuicConfig::default().into());
The portmapper_config() and net_report_config() methods (sourced from iroh/src/portmapper.rs) control NAT traversal helpers and periodic network diagnostics, defaulting to PortmapperConfig::default() and NetReportConfig::default() respectively.
Implementation Examples
Minimal Node with Production Relays
This example uses the N0 preset from iroh/src/endpoint.rs to quickly configure a node with sensible defaults for the number0 relay network.
use iroh::{Endpoint, endpoint::presets};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ep = Endpoint::builder(presets::N0)
.alpns(vec![b"my-app/1".to_vec()])
.relay_mode(iroh::RelayMode::Default)
.bind()
.await?;
println!("Node ID: {}", ep.id());
Ok(())
}
Custom Relay with Self-Signed TLS
For private infrastructure where relays use self-signed certificates, combine RelayMode::Custom with CaTlsConfig::insecure_skip_verify() as implemented at line 713 in endpoint.rs.
use iroh::{Endpoint, RelayMode, RelayUrl, tls::CaTlsConfig};
use std::str::FromStr;
#[tokio::main]
async fn main() -> iroh::Result<()> {
let relay = RelayUrl::from_str("https://my.relay.local:443")?;
let ep = Endpoint::builder(iroh::endpoint::presets::N0)
.relay_mode(RelayMode::Custom(vec![relay].into()))
.ca_tls_config(CaTlsConfig::insecure_skip_verify())
.alpns(vec![b"secure-proto".to_vec()])
.bind()
.await?;
println!("Connected to private relay: {}", ep.id());
Ok(())
}
DNS-Based Address Resolution
Enable DNS lookup services so you can connect to peers using only their EndpointId, as defined in iroh/src/address_lookup.rs.
use iroh::{Endpoint, address_lookup::DnsAddressLookupBuilder};
#[tokio::main]
async fn main() -> iroh::Result<()> {
let ep = Endpoint::builder(iroh::endpoint::presets::N0)
.alpns(vec![b"dns-enabled-app".to_vec()])
.address_lookup(DnsAddressLookupBuilder::default())
.bind()
.await?;
// Now connections can resolve DNS records for EndpointId
Ok(())
}
Custom QUIC Transport Only
Force the node to use only a specific QUIC configuration by clearing default transports and adding a custom one, referencing Builder::clear_ip_transports() (line 503) and Builder::add_custom_transport() (line 813).
use iroh::{Endpoint, transport::QuicConfig};
#[tokio::main]
async fn main() -> iroh::Result<()> {
let ep = Endpoint::builder(iroh::endpoint::presets::N0)
.clear_ip_transports()
.add_custom_transport(QuicConfig::default().into())
.bind()
.await?;
Ok(())
}
Summary
- Builder API: All iroh node configuration flows through
iroh::endpoint::Builderiniroh/src/endpoint.rs, using chainable methods that returnSelf. - Required Settings: You must configure
alpnsbefore accepting connections, and either accept auto-generatedSecretKeyvalues or provide persistent keys viasecret_key(). - Relay Modes: Default is
RelayMode::Disabled; useRelayMode::Defaultfor production number0 relays orRelayMode::Customfor private infrastructure. - TLS Security:
CaTlsConfig::default()trusts system roots, whileinsecure_skip_verify()enables testing against self-signed certificates. - Transport Control: Remove default IPv4/IPv6 sockets with
clear_ip_transports()and inject custom configurations viaadd_custom_transport(). - Finalization: Every configuration chain must end with
bind().awaitto create the liveEndpointand open network sockets.
Frequently Asked Questions
What happens if I don't configure a secret key?
If you omit secret_key(), the builder automatically generates a fresh SecretKey using Default::default() when calling Builder::empty() or any preset. This creates a new node identity on every restart, which is suitable for ephemeral clients but prevents persistent peer recognition for server nodes.
Why is ALPN configuration required for incoming connections?
The alpns vector is empty by default, and the builder rejects incoming connections unless at least one application-layer protocol identifier is specified. This security measure ensures the node only accepts connections for explicitly supported protocols, preventing protocol confusion attacks.
How do I configure iroh nodes for private relay infrastructure?
Use RelayMode::Custom with a vector of RelayUrl instances pointing to your private servers. If your private relays use self-signed certificates, chain ca_tls_config(CaTlsConfig::insecure_skip_verify()) before binding. For complete isolation from public relays, ensure you also configure ca_tls_config with appropriate trust anchors.
What is the default relay mode and when should I change it?
The default relay mode is RelayMode::Disabled, which prevents the node from using relay servers for NAT traversal. Change this to RelayMode::Default for production deployments to enable connectivity through the number0 relay network, or use RelayMode::Staging for testing against staging infrastructure. For air-gapped networks, keep it disabled and rely on direct addressing or custom transport configurations.
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 →