# pion/ice: the connectivity engine underneath a Go WebRTC stack

> Pion ICE is a Go implementation of the ICE connectivity protocol, the layer that decides which candidate address pair two peers will actually talk over, and it is the foundation of the Pion WebRTC stack. It suits a Go developer building peer-to-peer media or a data channel who wants the connection establishment handled for them, and it is not a media stack on its own.

**pion/ice** — A Go implementation of ICE

- Repository: https://github.com/pion/ice
- Website: https://pion.ly/
- Stars: 617 · Forks: 236
- Language: Go
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/pion-ice

## The layer that decides which address pair the peers use

Pion ICE is described as a Go implementation of ICE, and the readme places it in context with a single sentence: the library is used as a part of the Pion WebRTC implementation, and the WebRTC roadmap is the place to track the major milestones.

That framing matters, because ICE is the least glamorous and most load-bearing part of a peer-to-peer stack. Two peers behind NAT do not simply connect to each other; they each discover a set of candidate addresses, exchange them, run connectivity checks between pairs, and then agree on which pair to use. ICE is the protocol that makes that negotiation happen, and a peer-to-peer application that does not implement it will not work on a real network.

So the audience is a Go developer building something that needs peer-to-peer connectivity: a media call, a data channel, a file transfer, a game session. If you are writing that from scratch in Go, this library removes a substantial and fiddly piece of work. If you are building in another language, or you are using a WebRTC stack that already ships its own ICE, then the library has no role in your build.

The primary language is Go, the licence is MIT, the default branch is main, and the project page is pion.ly. The community channels are a Discord server and accounts on Bluesky and Twitter, and the readme also notes that commercial support and non-public APIs can be arranged by contacting the project.

The library is a component, not a product. That is the right way to judge it.

## Candidates, checks and nomination, as separate files

The repository listing is flat and every file name is a concept from the protocol, which makes it a good map of what the library actually does.

The candidate files are the heart of it. There is candidate.go as the shared type, then candidate_base.go, candidate_host.go for host candidates, candidate_relay.go for candidates reached through a TURN relay, candidate_peer_reflexive.go, and candidate_server_reflexive.go for the server reflexive address a NAT presents. candidatetype.go holds the enumeration, and candidatepair.go with candidatepair_state.go model the pairing of two candidates and the state machine that decides whether a pair is in progress, succeeded or failed. priority.go covers the priority rules that decide which pair is preferred.

Around that, agent.go and agent_handlers.go are the orchestration, with agent_options.go for configuration and agent_stats.go for the counters a monitoring system reads. The active TCP handling lives in active_tcp.go, the mDNS candidate discovery in mdns.go, and the gathering entry point in gather.go. networktype.go and addr.go handle the address classification, address_rewrite_rule.go the rewriting of addresses, and piggyback.go the STUN piggybacking behaviour. errors.go collects the error types, ice.go is the top level, and icecontrol.go is the control interface used to drive an agent from outside.

Every one of these files has a corresponding _test.go beside it, and there is a codecov.yml at the root along with a Go Report Card badge, so the project is set up to measure coverage rather than merely claim it.

The examples directory shows the intended usage patterns, and the names are instructive: automatic-renomination, continual-gathering, nat-rules, and ping-pong. Those cover the four behaviours that trip people up: a candidate that becomes invalid after nomination, a network interface that changes, a deployment behind a restrictive NAT, and the simplest possible case.

## The dependency list, which shows the shape of the stack

The go.mod for the v5 module is short and entirely composed of other Pion projects, with one exception.

The module path is github.com/pion/ice/v5, and it requires Go 1.24.0 or newer. The direct dependencies are github.com/pion/dtls/v3, which is the DTLS layer that secures the connection once ICE has chosen a path, github.com/pion/transport/v5 for the underlying transport, github.com/pion/turn/v5 for talking to TURN servers, github.com/pion/stun/v4 for the STUN messages ICE itself uses, github.com/pion/mdns/v2 for local candidate discovery, github.com/pion/logging and github.com/pion/randutil, plus github.com/google/uuid, github.com/stretchr/testify for the tests, golang.org/x/net and golang.org/x/sys.

The single non-Pion dependency of note is github.com/wlynxg/anet, listed as indirect, which is a library for parsing network addresses, including the awkward ones.

What this list tells you is that ICE in Pion is not a self-contained thing. It sits between the transport and the DTLS layer, and it expects a TURN and STUN implementation alongside it. Choosing pion/ice means choosing the Pion stack, and if you are already using pion/webrtc then that is a foregone conclusion rather than a decision.

The indirect entries are also worth a glance: go.yaml.in/yaml/v3, golang.org/x/crypto and golang.org/x/time. The crypto and time packages come in through the ICE dependency chain, which is the usual situation for a library that handles certificates and retransmission timing.

## Where pion/ice is the wrong choice

The first limitation is that this is not a WebRTC stack. There is no media handling here, no RTP, no data channel, no signalling. If the requirement is a video call, pion/webrtc is the project to evaluate, and this library is a component inside it. The readme is clear about this and points you at the WebRTC roadmap, but the repository name and the pion.ly domain make it easy to arrive with the wrong expectation.

The second is the version state, which is the thing an adopting team has to look at hardest. The module path in go.mod is already github.com/pion/ice/v5, and the package documentation badge points at the v5 reference, so the v5 API is the current one. But the visible release tags are a mix: v4.4.8 from 2026-10-19, v4.4.7 from 2026-10-07, and v5.0.0-rc.7 from 2026-10-06. A release candidate for v5 alongside patch releases on v4 means the v4 line is still being maintained while v5 settles, and the v5 migration is a breaking change to the import path for anyone on v4.

The third is that the library carries the complexity of the problem rather than hiding it. NAT traversal is genuinely hard, and a correct ICE implementation has a lot of state: the candidate types, the pair states, the priority rules, the nomination logic, the mDNS handling, address rewriting, TCP active candidates, and the timing of checks and retransmissions. You get that logic from a maintained project instead of writing it, and you still have to configure it correctly, because the failure modes of a misconfigured ICE agent are the classic symptoms of a connection that establishes and then goes nowhere.

The fourth is that this is a library for the peer side, and it says nothing about how you get the two peers to find each other in the first place. Signalling, the exchange of session descriptions, and the choice of TURN servers are all outside this repository, and a deployment that fails to connect is at least as likely to have failed there.

The fifth is licensing of the wider stack. This repository is MIT, and the other Pion components in the dependency list are also Pion projects under permissive terms, but the code you write on top of it inherits your own obligations, and a project that needs a supported version of WebRTC rather than a community one is looking for commercial support, which the readme says is available.

## Conclusion

pion/ice fits a Go developer who needs a peer-to-peer connection established without reimplementing ICE, since the library covers the candidate types, the connectivity checks, the nomination and the transport under the same MIT licence as the rest of Pion. It is the wrong choice if you need media handling, since this is the connectivity layer only, and it is the wrong choice if you need a versioned stable release today, because the module path is already at v5 while the visible release tags include a v5.0.0 release candidate alongside the v4.4.x line. Before adopting it, read the Pion WebRTC roadmap that the readme points to for the major milestones, confirm the v4 to v5 migration is acceptable for your build, and note that the last push was on 2026-10-20 with v4.4.8 published on 2026-10-19.

## FAQ

### What does pion/ice do?

It is a Go implementation of ICE, the connectivity protocol that lets two peers behind NAT negotiate which address pair they will talk over. It handles candidate gathering for host, server reflexive, peer reflexive and relay candidates, the connectivity checks between candidate pairs, the nomination state machine and the priority rules, and it is used as part of the Pion WebRTC implementation.

### How do I install pion/ice?

It is a Go module, so you add it to your go.mod like any other dependency. The module path is github.com/pion/ice/v5, it requires Go 1.24.0 or newer, and the API reference is published on pkg.go.dev for the v5 path.

### Which candidate types does pion/ice support?

The repository has separate source files for host candidates, relay candidates for TURN, peer reflexive candidates and server reflexive candidates, together with the candidate pairing and state machine, the priority rules, the mDNS discovery and the TCP active candidate handling.

### What examples does pion/ice ship?

Four: automatic-renomination, continual-gathering, nat-rules and ping-pong. Between them they cover the cases where a candidate goes invalid after nomination, where the network interface changes, where a restrictive NAT is in the way, and the simplest working case.

### Is pion/ice still being maintained?

The last push to the repository was on 2026-10-20, and the release tags include v4.4.8 from 2026-10-19 and v5.0.0-rc.7 from 2026-10-06, so the v4 line is still receiving patches while v5 is in release candidate. The licence is MIT.

## Sources

- [License: MIT](https://github.com/pion/ice/blob/main/LICENSE)
- [pion/ice on GitHub](https://github.com/pion/ice)
- [Project website](https://pion.ly/)
- [README](https://github.com/pion/ice/blob/main/README.md)
- [Releases](https://github.com/pion/ice/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pion-ice
