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::Builder in iroh/src/endpoint.rs, using chainable methods that return Self.
  • Required Settings: You must configure alpns before accepting connections, and either accept auto-generated SecretKey values or provide persistent keys via secret_key().
  • Relay Modes: Default is RelayMode::Disabled; use RelayMode::Default for production number0 relays or RelayMode::Custom for private infrastructure.
  • TLS Security: CaTlsConfig::default() trusts system roots, while insecure_skip_verify() enables testing against self-signed certificates.
  • Transport Control: Remove default IPv4/IPv6 sockets with clear_ip_transports() and inject custom configurations via add_custom_transport().
  • Finalization: Every configuration chain must end with bind().await to create the live Endpoint and 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:

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 →