# Optimizing iroh Connection Performance with Path Selection Strategies

> Boost iroh connection performance with path selection strategies. Discover how transport tiers, RTT biases, and hysteresis thresholds optimize routes for faster direct IP connections.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: performance
- Published: 2026-06-18

---

**iroh optimizes connection performance through a configurable `PathSelector` trait that evaluates network paths based on transport tiers and round-trip time (RTT) biases, automatically preferring direct IP routes over relays while using hysteresis thresholds to prevent connection flapping.**

Optimizing iroh connection performance with path selection strategies begins with understanding how the library manages multiple concurrent routes to a remote peer. The n0-computer/iroh repository implements a sophisticated path selection system in [`iroh/src/socket/biased_rtt_path_selector.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/biased_rtt_path_selector.rs) that automatically balances latency, stability, and connectivity across IPv4, IPv6, relay, and custom transports. By default, the library uses a biased RTT algorithm with sticky switching logic, but exposes a trait-based interface allowing complete customization of routing decisions.

## The Default BiasedRttPathSelector Algorithm

The built-in selector, `BiasedRttPathSelector`, is automatically instantiated for every endpoint via the builder's `path_selector` field in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (lines 1446-1454). This implementation groups candidate paths into hierarchical tiers and applies weighted latency comparisons to determine the optimal route.

### Transport Tier Prioritization

Paths are first categorized by transport type before any latency comparison occurs. The selector distinguishes between **Primary** paths (direct IP-based and custom transports) and **Backup** paths (relay-based). Primary paths always take precedence over backup paths regardless of RTT measurements, ensuring that direct connections are preferred when available. This tiering logic prevents unnecessary relay utilization when a direct route exists, even if the relay temporarily shows lower latency.

### RTT Bias and Hysteresis Logic

Within each transport tier, the selector calculates a **biased RTT** for every path. The implementation applies a configurable `IPV6_RTT_ADVANTAGE` of **3 milliseconds** to IPv6 paths, encouraging IPv6 adoption when latencies are otherwise equal. The core selection logic resides in the `select` method (lines 136-184 of [`biased_rtt_path_selector.rs`](https://github.com/n0-computer/iroh/blob/main/biased_rtt_path_selector.rs)), which implements sticky switching through the `RTT_SWITCHING_MIN` threshold of **5 milliseconds**. A path will only replace the current active path if its biased RTT is at least 5 ms better, preventing frequent path changes caused by network jitter. However, tier changes bypass this threshold—if a Primary path becomes available while a Backup path is active, the selector switches immediately.

## Implementing Custom Path Selection Strategies

Applications requiring specific routing policies can implement the `PathSelector` trait defined in [`iroh/src/socket/remote_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map.rs) (lines 1419-1443). This trait receives a `PathSelectionContext` containing the current set of paths, their measured RTTs, and transport metadata, and returns a `PathSelection` indicating which path should become active.

### The Builder API for Custom Selectors

The endpoint builder exposes a `path_selector` method in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (lines 1636-1642) that accepts `Arc<dyn PathSelector>`:

```rust
Builder::path_selector(Arc<dyn PathSelector>)

```

Because the selector is stored as an `Arc`, implementations can be shared across multiple endpoints, ensuring consistent path selection across an entire application. Custom implementations can enforce absolute priorities for specific transports (such as low-latency LAN interfaces), implement bandwidth-based heuristics, or incorporate historical success rates into routing decisions.

## Performance Impact of Path Selection

The selector implementation directly impacts three critical performance characteristics:

* **Latency Reduction** – By continuously evaluating biased RTT measurements, iroh rapidly converges on the fastest available IP route, minimizing time-to-first-byte for data streams.
* **Connection Stability** – The 5 ms hysteresis threshold eliminates path flapping that would otherwise trigger QUIC renegotiation and cause latency spikes during transient network jitter.
* **Automatic Fallback** – When all Primary paths become unavailable (due to NAT or firewall restrictions), the selector seamlessly transitions to Backup relay paths without manual intervention or application-level logic.

## Practical Implementation Examples

### Using the Default Selector

The default configuration requires no additional code—the `BiasedRttPathSelector` is installed automatically:

```rust
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    // BiasedRttPathSelector is automatically configured
    let ep = Endpoint::builder(presets::N0).bind().await?;
    Ok(())
}

```

### Creating a Custom Transport Preference

The following implementation prioritizes custom transport paths over standard IP routes:

```rust
use std::{
    sync::Arc,
    time::Duration,
};
use iroh::{
    endpoint::{Builder, presets},
    socket::{
        remote_map::{PathSelection, PathSelectionContext, PathSelectionData, PathSelector},
        transports::{Addr, AddrKind, FourTuple, custom::CustomTransport},
    },
};

struct PreferCustomTransport;

impl PathSelector for PreferCustomTransport {
    fn select(&self, ctx: &PathSelectionContext<'_>) -> PathSelection {
        let mut best = None;
        for psd in ctx.paths() {
            if let Some(stats) = psd.stats() {
                let addr = psd.network_path();
                if matches!(addr.remote(), Addr::Custom(_)) {
                    best = Some((psd, stats.rtt));
                    break;
                }
            }
        }
        
        if let Some((psd, _)) = best {
            let mut sel = PathSelection::none();
            sel.set(psd);
            sel
        } else {
            ctx.paths().next().map(|psd| {
                let mut sel = PathSelection::none();
                sel.set(psd);
                sel
            }).unwrap_or_default()
        }
    }
}

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let custom_selector = Arc::new(PreferCustomTransport);
    let ep = Builder::new(presets::N0)
        .path_selector(custom_selector)
        .bind()
        .await?;
    Ok(())
}

```

### Adjusting RTT Biases

You can modify the default selector's bias parameters to favor specific transport types:

```rust
use std::time::Duration;
use iroh::socket::biased_rtt_path_selector::{BiasedRttPathSelector, TransportBias};
use iroh::socket::transports::AddrKind;
use std::sync::Arc;

let selector = BiasedRttPathSelector::default()
    .with_bias(AddrKind::IpV4, TransportBias::primary().with_rtt_advantage(Duration::from_millis(10)));

let ep = iroh::endpoint::Builder::new(presets::N0)
    .path_selector(Arc::new(selector))
    .bind()
    .await?;

```

This configuration gives IPv4 a 10 ms advantage over IPv6, making IPv4 the preferred path when round-trip times are comparable.

## Summary

- **iroh** evaluates multiple network paths (IPv4, IPv6, relay, custom) through the `PathSelector` trait defined in [`iroh/src/socket/remote_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map.rs).
- The default `BiasedRttPathSelector` in [`iroh/src/socket/biased_rtt_path_selector.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/biased_rtt_path_selector.rs) prioritizes Primary transports over Backup relays and applies a 3 ms IPv6 advantage with 5 ms sticky switching hysteresis.
- Custom selectors can be injected via `Builder::path_selector()` in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) using `Arc<dyn PathSelector>` for shared state across endpoints.
- The tier-based logic ensures immediate failover to relays when direct paths fail, while RTT bias prevents flapping during network jitter.
- Production deployments can optimize for specific topologies by adjusting transport biases or implementing entirely custom selection algorithms.

## Frequently Asked Questions

### What is the PathSelector trait in iroh?

The `PathSelector` trait is a Rust interface defined in [`iroh/src/socket/remote_map.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/remote_map.rs) that allows applications to customize how iroh chooses between multiple network paths to a remote peer. It receives a `PathSelectionContext` containing candidate paths with their RTT statistics and transport types, and returns which path should become active. The default implementation is `BiasedRttPathSelector`, but any type implementing this trait can be injected via the endpoint builder.

### How does iroh prevent path flapping during network jitter?

iroh prevents path flapping through the `RTT_SWITCHING_MIN` constant in `BiasedRttPathSelector`, which requires a new path to have a biased RTT at least 5 milliseconds better than the current path before switching. This hysteresis threshold filters out minor latency variations caused by network jitter that would otherwise trigger unnecessary path changes and QUIC renegotiation.

### Can I prioritize relay connections over direct IP paths in iroh?

Yes, you can implement a custom `PathSelector` that inverts the default tier logic. While the built-in `BiasedRttPathSelector` always prefers Primary (direct IP) paths over Backup (relay) paths, a custom implementation can check for relay paths first in its `select` method and return those preferentially. You would inject this custom selector using `Builder::path_selector()` in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).

### What is the default IPv6 advantage in iroh path selection?

The default `BiasedRttPathSelector` applies a 3 millisecond advantage to IPv6 paths through the `IPV6_RTT_ADVANTAGE` constant. This means that when IPv4 and IPv6 paths have identical measured RTTs, the selector will choose IPv6 because its effective biased RTT is 3 ms lower. This bias can be adjusted or removed when constructing a custom `BiasedRttPathSelector` instance using the `with_bias()` method.