# How to Configure iroh Nodes: Complete Builder API Guide

> Configure iroh nodes effectively using the chainable Builder API. Learn to set secret keys, ALPNs, and relay modes for seamless endpoint instantiation.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-07-12

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.

```rust
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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/defaults.rs).

```rust
// 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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/endpoint.rs)) enables DNS-based or PKARR-based peer discovery. Without this, you must provide direct addressing information for every connection.

```rust
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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) to quickly configure a node with sensible defaults for the number0 relay network.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/endpoint.rs).

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs).

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

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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.