# How to Configure Portmapper for NAT Traversal in iroh

> Configure portmapper for NAT traversal in iroh Discover how to map local services behind NAT using PortMapper::new and map_port for relay connections. Release ports with unmap.

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

---

**Use `PortMapper::new()` to connect to a relay, then call `map_port()` with your protocol and private address to expose a local service behind NAT through a public port allocated by the relay, and release it with `unmap()` when done.**

Configuring portmapper for NAT traversal in iroh allows nodes behind restrictive firewalls to expose local services to the public internet without manual router configuration. The `n0-computer/iroh` repository provides a built-in **Portmapper** service that handles dynamic port allocation and encrypted traffic forwarding. This guide explains how to use the client API defined in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs) to create and manage NAT mappings.

## Understanding iroh's Portmapper Architecture

The portmapper consists of a client-side API and a relay service that work together to punch holes through NAT.

### Core Components

- **`PortMapper` struct** – Defined in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs), this is the client interface that manages connections to relay nodes and tracks active mappings.
- **`Protocol` enum** – Supports `Protocol::Tcp` and `Protocol::Udp` for creating transport-specific mappings.
- **`PortMappingRequest` and `PortMappingResponse`** – Serialization structs that handle the negotiation between client and relay using `bincode` for low-overhead messaging.
- **Relay service** – A publicly reachable iroh node that allocates public ports and forwards traffic over the encrypted iroh mesh.

### How NAT Traversal Works

When you configure portmapper for NAT traversal, the client sends a `PortMappingRequest` to a relay containing the desired protocol and private socket address. The relay checks its available port pool, reserves an unused public port, and stores the mapping in an internal `HashMap<PublicPort, MappingInfo>` protected by a `RwLock`. The relay then returns a `PortMappingResponse` with the public endpoint. Incoming traffic on the relay's public port is forwarded through the iroh transport layer to your local service, and replies travel back through the same encrypted channel.

## Configuring the Portmapper Client

To begin, instantiate the `PortMapper` with the URL of a relay running the portmapper service.

```rust
use iroh::portmapper::{PortMapper, Protocol};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Connect to a public relay
    let mut mapper = PortMapper::new("https://relay.example.com:443").await?;
    
    // Map a local TCP service
    let public_addr = mapper
        .map_port(Protocol::Tcp, "127.0.0.1:8080".parse()?)
        .await?;
    
    println!("Public address: {}", public_addr);
    Ok(())
}

```

The `PortMapper::new` method establishes the connection to the relay, while `map_port` registers your local service. According to the source in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs), `map_port` is typically found around line 78 and handles the async negotiation with the relay.

### Mapping UDP Services

For UDP-based applications like DNS or VoIP, use the same pattern with `Protocol::Udp`:

```rust
let public_addr = mapper
    .map_port(Protocol::Udp, "127.0.0.1:5353".parse()?)
    .await?;

```

The implementation in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs) supports both protocols through the same async interface, spawning appropriate forwarding tasks for each transport type.

## Managing Port Mappings with map_port and unmap

Creating a mapping reserves a public port for the lifetime of the connection or until explicitly released. To free the port on the relay and stop the forwarding task, call `unmap`.

```rust
// Release the specific port when no longer needed
mapper.unmap(public_addr.port()).await?;

```

The `unmap` method, located in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs) around line 102, sends a teardown request to the relay, which removes the entry from its internal mapping table and closes the public socket.

## Complete Example: Exposing a Local HTTP Server Behind NAT

This full example demonstrates running a Hyper HTTP server locally and exposing it through the portmapper.

```rust
use iroh::portmapper::{PortMapper, Protocol};
use hyper::{
    service::{make_service_fn, service_fn},
    Body, Request, Response, Server,
};

async fn hello(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    Ok(Response::new(Body::from("Hello from behind NAT!")))
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Start local HTTP server on 127.0.0.1:8080
    let make_svc = make_service_fn(|_| async { 
        Ok::<_, hyper::Error>(service_fn(hello)) 
    });
    let server = Server::bind(&"127.0.0.1:8080".parse()?).serve(make_svc);
    tokio::spawn(server);

    // Configure portmapper for NAT traversal
    let mut mapper = PortMapper::new("https://relay.example.com:443").await?;
    let public_addr = mapper
        .map_port(Protocol::Tcp, "127.0.0.1:8080".parse()?)
        .await?;
    
    println!("Service available at: http://{}", public_addr);
    
    // Keep running until interrupted
    tokio::signal::ctrl_c().await?;
    
    // Clean up the mapping
    mapper.unmap(public_addr.port()).await?;
    Ok(())
}

```

This example combines the local service binding with the portmapper client, creating a complete NAT traversal solution using only the iroh SDK.

## Summary

- **Instantiate** the client with `PortMapper::new(relay_url)` to connect to a relay node.
- **Create mappings** using `map_port(Protocol, SocketAddr)` to expose local TCP or UDP services through a public port allocated by the relay.
- **Traffic forwarding** happens automatically over iroh's encrypted transport layer, with the relay handling the NAT traversal.
- **Clean up** resources by calling `unmap(port)` to release public ports and stop forwarding tasks.
- All client functionality is implemented in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs), with supporting relay logic in [`iroh/src/relay.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/relay.rs) and transport encryption in [`iroh/src/transport.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/transport.rs).

## Frequently Asked Questions

### What protocols does the iroh portmapper support?

The iroh portmapper supports both **TCP** and **UDP** protocols through the `Protocol` enum defined in [`iroh/src/portmapper.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/portmapper.rs). You can specify `Protocol::Tcp` for HTTP servers or `Protocol::Udp` for DNS, VoIP, or gaming applications.

### How do I choose a relay node for port mapping?

Any iroh node running the portmapper service can act as a relay. In practice, you should use a publicly reachable node with a stable internet connection and static IP address. The `n0-computer/iroh` repository typically provides documentation on public relay endpoints, or you can run your own relay by enabling the portmapper service in the node configuration.

### Is traffic forwarded through the portmapper encrypted?

Yes. While the portmapper handles the allocation of public ports and packet forwarding, the actual data flows through iroh's existing **encrypted transport layer** defined in [`iroh/src/transport.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/transport.rs). This means connections benefit from end-to-end encryption without requiring additional TLS configuration on the mapped ports.

### Can I use multiple port mappings simultaneously?

Yes. A single `PortMapper` instance can manage multiple concurrent mappings. Simply call `map_port()` multiple times with different local socket addresses or protocols. Each call returns a unique public address, and the client maintains an internal mapping table to route traffic correctly. Remember to call `unmap()` for each port when shutting down services.