# How to Implement Custom Transport Configurations in iroh

> Learn to implement custom transport configurations in iroh. Utilize the CustomTransport trait and Endpoint builder for flexible network setups in your Rust applications.

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

---

**You configure custom transports in iroh by implementing the `CustomTransport` trait and injecting it via `Endpoint::builder()`, while QUIC-specific settings are controlled through the `QuicTransportConfig` builder pattern.**

The iroh networking stack provides a flexible transport abstraction that allows developers to customize both the underlying QUIC protocol parameters and inject entirely custom transport implementations. Whether you need to tune idle timeouts and keep-alive intervals or implement a specialized transport for testing or edge deployments, the configuration API in `n0-computer/iroh` exposes these capabilities through the `EndpointBuilder` and transport-specific configuration structs.

## Core Transport Components

The transport system in iroh centers on three primary types defined in the source code.

### TransportConfig Enum

Located in [`iroh/src/socket/transports.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports.rs), the `TransportConfig` enum defines the available transport types:

- **Ip**: Standard UDP/QUIC sockets binding to specific addresses
- **Relay**: Transport through relay servers for NAT traversal  
- **Custom**: User-provided implementations via `Arc<dyn CustomTransport>`

### QuicTransportConfig Builder

The `QuicTransportConfig` struct in [`iroh/src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/quic.rs) provides a builder pattern for tuning QUIC-specific parameters. This wrapper around `noq::TransportConfig` exposes methods like `keep_alive_interval()`, `max_idle_timeout()`, and `mtu_discovery_config()` to control connection lifecycle and performance characteristics.

### CustomTransport Trait

Also defined in [`iroh/src/socket/transports.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports.rs), the `CustomTransport` trait requires implementors to provide three asynchronous methods. According to the source, you must implement:

```rust
async fn dial(&self, addr: TransportAddr) -> Result<Connection>;
async fn listen(&self, bind: TransportAddr) -> Result<Listener>;
fn local_addr(&self) -> TransportAddr;

```

These methods are called by the endpoint when it needs to open new paths or accept incoming connections.

## Implementing Custom QUIC Transport Settings

To customize QUIC-level parameters such as keep-alive intervals and idle timeouts, use the `QuicTransportConfig::builder()` method from [`iroh/src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/quic.rs).

```rust
use iroh::endpoint::{QuicTransportConfig, Endpoint};
use std::time::Duration;

// Build a QUIC config with a 15-second keep-alive and a 30-second idle timeout
let quic_cfg = QuicTransportConfig::builder()
    .keep_alive_interval(Duration::from_secs(15))
    .max_idle_timeout(Duration::from_secs(30))
    .build();

// Build the endpoint, injecting the custom QUIC config
let secret_key = iroh::SecretKey::generate();
let endpoint = Endpoint::builder(secret_key)
    .with_transport_config(quic_cfg) // custom QUIC settings
    .build()
    .await?;

```

The `with_transport_config()` method on `EndpointBuilder` (defined in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)) accepts this configuration and applies it to all QUIC connections created by the endpoint.

## Creating a Custom Transport Implementation

To implement a completely custom transport, you must satisfy the `CustomTransport` trait and pass it to the builder using `with_custom_transport()`. A complete working example is available in [`iroh/examples/custom-transport.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/custom-transport.rs).

```rust
use std::{sync::Arc, net::SocketAddr};
use iroh::{
    endpoint::{Endpoint, TransportConfig},
    socket::{CustomTransport, TransportAddr, Connection, Listener},
    SecretKey,
};

#[derive(Clone)]
struct MyTransport;

#[async_trait::async_trait]
impl CustomTransport for MyTransport {
    async fn dial(&self, addr: TransportAddr) -> anyhow::Result<Connection> {
        // Implement dialing logic here
        unimplemented!()
    }

    async fn listen(&self, bind: TransportAddr) -> anyhow::Result<Listener> {
        // Implement listening logic here  
        unimplemented!()
    }

    fn local_addr(&self) -> TransportAddr {
        // Return the address this transport advertises
        TransportAddr::Custom(iroh::socket::CustomAddr::new(0xdeadbeef, b"myaddr"))
    }
}

// Build the endpoint with the custom transport
let secret_key = SecretKey::generate();
let endpoint = Endpoint::builder(secret_key)
    .with_custom_transport(Arc::new(MyTransport))
    .build()
    .await?;

```

The `TransportConfig::Custom` variant wraps your implementation in an `Arc<dyn CustomTransport>`, allowing the endpoint to manage path selection across standard and custom transports simultaneously.

## Combining QUIC Settings and Custom Transports

You can combine custom QUIC configurations with custom transport implementations by calling both builder methods. This approach is useful when your custom transport still utilizes QUIC semantics but requires specialized dialing logic.

```rust
use std::{sync::Arc, time::Duration};
use iroh::{
    endpoint::{Endpoint, QuicTransportConfig},
    socket::{CustomTransport, TransportConfig},
    SecretKey,
};

// Configure QUIC parameters
let quic_cfg = QuicTransportConfig::builder()
    .keep_alive_interval(Duration::from_secs(10))
    .max_idle_timeout(Duration::from_secs(20))
    .build();

let secret_key = SecretKey::generate();
let endpoint = Endpoint::builder(secret_key)
    .with_transport_config(quic_cfg)               // custom QUIC knobs
    .with_custom_transport(Arc::new(MyTransport)) // custom transport
    .build()
    .await?;

```

The endpoint combines these configurations in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), creating sockets with the specified QUIC parameters while maintaining your custom transport in the available path list.

## Selecting Only Custom Transports

To disable the default IP transports (IPv4/IPv6) and use only your custom implementation, call `clear_ip_transports()` before building.

```rust
use iroh::{
    endpoint::{Endpoint, QuicTransportConfig},
    socket::TransportConfig,
    SecretKey,
};
use std::sync::Arc;

let secret_key = SecretKey::generate();

// Disable default IP transports, keep only the custom one
let endpoint = Endpoint::builder(secret_key)
    .clear_ip_transports()                     // removes IPv4/IPv6 transports
    .with_custom_transport(Arc::new(MyTransport))
    .build()
    .await?;

```

This method, defined in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), empties the default `Vec<TransportConfig>` that normally contains the IP bindings, allowing your custom transport to serve as the sole networking path.

## Summary

- **TransportConfig** in [`iroh/src/socket/transports.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports.rs) defines the three transport types: Ip, Relay, and Custom.
- **QuicTransportConfig** provides a builder pattern in [`iroh/src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/quic.rs) for tuning QUIC-specific parameters like timeouts and keep-alive intervals.
- **CustomTransport** trait requires `dial()`, `listen()`, and `local_addr()` implementations to create user-defined transports.
- Use `Endpoint::builder(secret_key).with_transport_config()` for QUIC settings and `with_custom_transport()` for custom implementations.
- Call `clear_ip_transports()` to remove default IP bindings when using exclusively custom transports.
- Reference [`iroh/examples/custom-transport.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/custom-transport.rs) for a complete working implementation.

## Frequently Asked Questions

### What is the difference between QuicTransportConfig and CustomTransport?

**QuicTransportConfig** tunes the QUIC protocol parameters (timeouts, MTU, congestion control) for standard UDP sockets, while **CustomTransport** is a trait that lets you implement entirely new transport mechanisms with custom dialing and listening logic. You can use `QuicTransportConfig` with the default IP transport or alongside custom transports, but `CustomTransport` requires implementing the full connection lifecycle yourself.

### How do I disable default IP transports when using a custom transport?

Call the `clear_ip_transports()` method on the `EndpointBuilder` before invoking `with_custom_transport()`. This removes the default IPv4 and IPv6 bindings from the internal `Vec<TransportConfig>`, ensuring only your custom transport handles network traffic. The method is defined in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) and takes no arguments.

### Can I combine multiple custom transports with standard QUIC?

Yes. The endpoint accepts a `Vec<TransportConfig>` that can contain multiple entries. You can call `with_custom_transport()` multiple times with different implementations, and the endpoint will treat them as distinct path options alongside standard IP and Relay transports. The `TransportConfig::Custom` variant wraps each implementation in an `Arc<dyn CustomTransport>` for efficient sharing across connections.

### Where can I find a complete working example of a custom transport?

The repository includes a full implementation in [`iroh/examples/custom-transport.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/custom-transport.rs). This example demonstrates proper trait implementation for `CustomTransport`, including error handling with `anyhow::Result` and the correct `TransportAddr` construction. The test suite in [`iroh/tests/patchbay/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/patchbay/util.rs) also shows real-world usage of `QuicTransportConfig::builder()` around line 464.