# How to Set Up DNS/Pkarr for iroh Endpoint Connections Using Only an EndpointId

> Learn to set up DNSPkarr for iroh endpoint connections using just an EndpointId. Connect to iroh peers seamlessly by configuring a PkarrResolver and querying dns.iroh.link for automatic address resolution.

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

---

**You can connect to an iroh peer using only its EndpointId by configuring a `PkarrResolver` that queries the public DNS/PKARR relay at `dns.iroh.link` to resolve the peer's network addresses automatically.**

The iroh networking library (n0-computer/iroh) enables peer-to-peer connections without manual IP configuration by leveraging cryptographic peer identities. When you need to set up DNS/Pkarr for iroh endpoint connection using only an EndpointId, the library provides built-in resolvers that query public relays to translate public keys into routable addresses. This approach eliminates the need for static IP addresses or custom DNS infrastructure.

## Understanding iroh's DNS and PKARR Resolution Architecture

iroh resolves peer addresses through two complementary HTTP-based services that map an **EndpointId** (the peer's public key) to IP/port pairs:

- **DNS-over-HTTPS**: The `dns.iroh.link` server publishes TXT records containing address lists. The client implementation lives in [`iroh/src/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/util.rs) as the `DnsResolver` type.
- **PKARR Relay**: Stores signed packets with address information at `https://dns.iroh.link/pkarr`, accessible without traditional DNS. Implemented in [`iroh/src/address_lookup/pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/pkarr.rs) as the `PkarrResolver` type.

The PKARR relay also functions as a DNS resolver, so clients only need a single HTTPS-based resolver to retrieve full address lists. Both production and staging environments are available, with the production URL defined as `N0_DNS_PKARR_RELAY_PROD` in the source code.

## Step-by-Step Implementation

### 1. Create the Local Endpoint

Initialize a QUIC endpoint that will handle outgoing connections. According to [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), the `Endpoint::bind()` method creates the local socket:

```rust
let endpoint = Endpoint::bind().await?;

```

### 2. Prepare TLS Configuration

The HTTPS resolver requires a `rustls::ClientConfig` to establish secure connections to the relay. Build a configuration with standard root certificates:

```rust
let mut roots = RootCertStore::empty();
roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
    rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
        ta.subject,
        ta.spki,
        ta.name_constraints,
    )
}));
let tls_config = ClientConfig::builder()
    .with_safe_defaults()
    .with_root_certificates(roots)
    .with_no_client_auth();

```

### 3. Build the PKARR Resolver

Use the `PkarrResolver::n0_dns()` constructor to create a resolver pre-configured with the production relay URL. As implemented in [`iroh/src/address_lookup/pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/pkarr.rs) at lines 127-132, this method references the `N0_DNS_PKARR_RELAY_PROD` constant:

```rust
let resolver = PkarrResolver::n0_dns().build(tls_config)?;

```

### 4. Attach the Resolver to the Endpoint

The `Endpoint` struct stores the resolver in its `dns_resolver` field (defined in [`iroh/src/socket.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket.rs) at line 174). Replace the default resolver with your PKARR instance:

```rust
endpoint.set_address_lookup(resolver);

```

### 5. Connect Using Only the EndpointId

With the resolver attached, initiate connections using only the peer's public key. The resolver automatically fetches and verifies the signed PKARR packet, extracts the address list, and attempts QUIC connections:

```rust
let conn = endpoint
    .connect(endpoint_id, ALPN)   // endpoint_id is the peer's EndpointId
    .await?;

```

## Complete Integration Example

This minimal example demonstrates the full flow from binding an endpoint to establishing a connection using only an EndpointId:

```rust
use iroh::endpoint::{Endpoint, EndpointId};
use iroh::address_lookup::pkarr::PkarrResolver;
use rustls::{ClientConfig, RootCertStore};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 1. Create a local endpoint
    let endpoint = Endpoint::bind().await?;

    // 2. Prepare TLS config for HTTPS resolver
    let mut roots = RootCertStore::empty();
    roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
        rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
            ta.subject,
            ta.spki,
            ta.name_constraints,
        )
    }));
    let tls_config = ClientConfig::builder()
        .with_safe_defaults()
        .with_root_certificates(roots)
        .with_no_client_auth();

    // 3. Build the PKARR resolver targeting the public relay
    let pkarr_resolver = PkarrResolver::n0_dns().build(tls_config)?;

    // 4. Attach resolver to endpoint
    endpoint.set_address_lookup(pkarr_resolver);

    // 5. Remote peer's EndpointId (obtained out-of-band)
    let remote_id: EndpointId = "...".parse()?; // Replace with actual ID

    // 6. Connect - resolver automatically looks up addresses
    const ALPN: &[u8] = b"iroh-example/echo/0";
    let conn = endpoint.connect(remote_id, ALPN).await?;

    // 7. Use the connection
    let (mut send, mut recv) = conn.open_bi().await?;
    send.write_all(b"hello from PKARR!").await?;
    send.finish()?;
    let reply = recv.read_to_end(1024).await?;
    println!("peer replied: {}", String::from_utf8_lossy(&reply));

    Ok(())
}

```

## Key Source Files and Implementation Details

| File | Purpose | Key Components |
|------|---------|----------------|
| [`iroh/src/address_lookup/pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/pkarr.rs) | PKARR resolution logic | `PkarrResolver`, `N0_DNS_PKARR_RELAY_PROD` constant (lines 127-132), packet verification (lines 115-126) |
| [`iroh/src/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/util.rs) | DNS resolver types | `DnsResolver` type definition |
| [`iroh/src/socket.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket.rs) | Endpoint state management | `dns_resolver` field storage (line 174) |
| [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) | Core endpoint API | `Endpoint::bind()` implementation (line 62) |
| [`iroh/tests/integration.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/integration.rs) | Real-world usage examples | `PkarrResolver::n0_dns()` usage (line 95) |

## Summary

- **EndpointId represents identity**: The peer's public key serves as the sole identifier needed for connection.
- **PKARR eliminates manual addressing**: The `PkarrResolver` automatically resolves EndpointIds to IP/port pairs via the n0-dns relay.
- **Single resolver required**: The PKARR relay doubles as a DNS resolver, requiring only one HTTPS client configuration.
- **Built-in verification**: Signed packets are verified automatically before addresses are returned to the endpoint.
- **No infrastructure needed**: The public `dns.iroh.link` service handles publication and resolution without requiring custom DNS servers.

## Frequently Asked Questions

### What is an EndpointId in iroh?

An **EndpointId** is the 32-byte public key derived from the peer's secret key, serving as the permanent cryptographic identity of an iroh endpoint. According to the n0-computer/iroh source code, this identifier remains stable even as the peer moves between networks or changes IP addresses, making it the only piece of information required to initiate a connection when using DNS/PKARR resolution.

### Do I need to run my own DNS server to use PKARR resolution?

No. The `PkarrResolver::n0_dns()` constructor configures your endpoint to use the public n0-dns relay at `https://dns.iroh.link/pkarr`, maintained by number-0. This relay stores signed PKARR packets and serves DNS TXT records, allowing you to resolve peer addresses without deploying any custom DNS infrastructure or maintaining static IP mappings.

### How does the PkarrResolver verify the signed packets?

The resolver verifies cryptographic signatures on PKARR packets before extracting addresses, as implemented in [`iroh/src/address_lookup/pkarr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup/pkarr.rs) between lines 115-126. Each packet contains a signed statement binding the EndpointId to specific network addresses, ensuring that only the peer holding the corresponding private key can publish or update their address records, preventing man-in-the-middle attacks during address resolution.

### Can I use a custom PKARR relay instead of the public n0-dns service?

Yes. While `PkarrResolver::n0_dns()` provides a convenient pre-configured resolver using the production relay, you can instantiate `PkarrResolver` with custom URLs pointing to your own PKARR relay infrastructure. This allows private networks or specialized deployments to maintain isolated address resolution systems while using the same underlying `EndpointId`-based connection logic.