# How to Gracefully Close iroh Endpoint and Connections

> Learn to gracefully close iroh endpoint and connections. Discover how to use Endpoint::close() and Connection::close() for clean shutdowns in your n0-computer/iroh projects.

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

---

**Call `Endpoint::close().await` to initiate graceful shutdown on the endpoint, and use `Connection::close(error_code, reason)` followed by `Connection::closed().await` to shut down individual QUIC connections cleanly.**

When building peer-to-peer applications with the iroh networking stack, properly releasing resources is critical to prevent connection leaks and ensure all QUIC close frames are transmitted. This guide explains how to gracefully close iroh Endpoint and connections using the specific APIs exposed in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) and [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs).

## Gracefully Closing Individual Connections

Connection objects expose two primary methods for graceful termination: `close()` to signal the remote peer, and `closed()` to await the final teardown.

### Sending a Close Signal

The `Connection::close` method accepts a QUIC error code and an optional reason byte slice, forwarding the request to the underlying QUIC connection:

```rust
pub fn close(&self, error_code: VarInt, reason: &[u8]) {
    self.inner.quic_conn.close(error_code, reason);
}

```

Located in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) around line 185, this method initiates the QUIC close process without blocking.

### Waiting for Confirmation

To block until the connection is fully terminated, await the `closed()` future:

```rust
pub async fn closed(&self) -> ConnectionError {
    self.inner
        .closed
        .await
        .expect("closed sender dropped")
}

```

This asynchronous method, found near line 170 in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs), resolves when the connection enters a terminal state, returning the `ConnectionError` that caused the closure.

## Shutting Down the Endpoint

The `Endpoint` struct provides similar semantics for graceful shutdown at the listener level.

### Initiating Endpoint Closure

Call `Endpoint::close()` to gracefully shutdown the endpoint. This method closes all outgoing connections and waits for ongoing connections to finish:

```rust
pub async fn close(&self) {
    // Closes all outgoing connections and waits for ongoing connections to finish
    self.inner.close_all_connections().await;
    self.inner.shutdown().await;
}

```

As implemented in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) around line 1690, this async function ensures no new connections are accepted and signals existing ones to terminate.

### Awaiting Full Termination

After calling close, you can await the `closed()` future to ensure all background tasks complete:

```rust
pub fn closed(&self) -> EndpointClosed {
    self.inner.closed()
}

```

This method returns an `EndpointClosed` future that resolves when the endpoint's internal resources are fully released.

## Complete Shutdown Sequence

A robust shutdown follows this order:

1. Close all active connections using `connection.close(0u32.into(), b"shutting down")`
2. Await each connection's termination with `connection.closed().await`
3. Call `endpoint.close().await` to stop accepting new connections
4. Await `endpoint.closed().await` to ensure the socket and background tasks are cleaned up
5. Drop the endpoint to release remaining resources

## Implementation Details

The graceful shutdown logic resides in specific source files:

- [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs): Contains `Connection::close` (line ~185) and `Connection::closed` (line ~170)
- [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs): Implements `Endpoint::close` (line ~1690) and `Endpoint::closed`
- The `close` methods forward to the underlying QUIC implementation via `self.inner.quic_conn.close()`
- The `closed` futures monitor internal channels that signal when the QUIC stack reports termination

## Summary

- **Use `Connection::close(error_code, reason)`** to send a QUIC close frame with a specific error code and optional reason string
- **Await `Connection::closed()`** to block until the connection is fully terminated and retrieve the close reason
- **Call `Endpoint::close().await`** to initiate graceful shutdown, which closes all connections and stops accepting new ones
- **Await `Endpoint::closed()`** to ensure all background tasks and socket resources are released before dropping the endpoint

## Frequently Asked Questions

### What error code should I pass to Connection::close?

Pass `0u32.into()` (converted to `VarInt`) for a normal, error-free shutdown. Non-zero codes indicate application-specific errors or abrupt termination.

### Does Endpoint::close wait for all connections to close?

Yes. According to the implementation in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), the `close` method waits for ongoing connections to finish and closes all outgoing connections before returning.

### Can I reuse an Endpoint after calling close?

No. The `close` method is a terminal operation. The endpoint will not be restarted; you must create a new `Endpoint` instance if you need to resume networking.

### What happens if I don't await Connection::closed?

The connection will still close, but you won't receive confirmation of when the peer acknowledged the close or the final error state. Always await `closed()` in critical paths to ensure clean resource reclamation.