Open-source project
pion/turn avatar
pion/turn

pion/turn: relay traversal you compile instead of configure

Pion TURN, an API for building TURN clients and servers

2,269 stars374 forksGoMIT

At a glance

What is it?
pion/turn is a Go API for building TURN servers and clients, not a relay daemon you install and edit. That buys runtime control over allocations and costs you a Go 1.24 floor, three sibling modules to keep aligned, and a README that has drifted from the module path.
Who is it for?
pion/turn fits when the relay must live inside a Go service you already operate, since allocations and listeners become Go values instead of config file edits. It does not fit when you want a package to install and point at from a separate host, because the project ships no daemon, only examples you compile.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • pion/turn2,269 ★ · +1
Star history
Repository2026-10-132026-10-23
pion/turn2.3k2.3k

1 Stars in the last 10 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Pion hands you relay traversal as a Go API

Peers behind carrier-grade NAT cannot open a direct path to each other, so a WebRTC stack falls back to a relay that receives traffic on one socket and forwards it on another. `pion/turn` supplies that relay and its client side in Go. `server.go` and `client.go` sit at the repository root next to `server_config.go` and `stun_conn.go`, which is the shape of a library rather than of a daemon with a config grammar.

Its Using section is blunt about that choice: `pion/turn` is an API for building STUN/TURN clients and servers, not a binary you deploy then configure. That reader is an engineer who already runs a Go service and wants allocations inside it, not an operator who wants a relay package on another host. You can still ship a separate process, but you would compile one of the examples first, and the README says you may be able to download pre-made binaries of those examples if you want to get started quickly.

Three relay address generators decide where the relay listens

A relay socket is the piece of state a server hands out. Three files at the repository root implement three ways to produce that address: `relay_address_generator_none.go`, `relay_address_generator_range.go` and `relay_address_generator_static.go`. Names describe the strategy and nothing more, and the README says nothing about their behaviour, so choosing between them means reading those three files. One takes a fixed address, another takes a range, the third is the empty case.

Everything else at the root is small and legible. `addr_family.go` handles address families, `errors.go` holds the error values, `lt_cred.go` handles long-term credentials, and `internal/` keeps the parts the project does not expose. Tests sit next to what they cover in `client_test.go`, `server_test.go` and `lt_cred_test.go`, while platform differences are isolated in `helpers_unix_test.go` and `helpers_windows_test.go`. A separate `e2e/` directory sits beside those tests for end-to-end runs.

Building one example directory is the whole install step

There is no install command to copy, and the README does not provide one. It says to run `go build` in the directory of the example you care about, and it points at the `examples` directory and at godoc.org/github.com/pion/turn for the quickest start. Five example trees are checked in: `examples/turn-server`, `examples/turn-client`, `examples/stun-only-server`, `examples/mutual-tls-auth` and `examples/lt-cred-generator`. Begin with `examples/stun-only-server` if you only need to answer discovery traffic, then move to `examples/turn-server`, because the simple server is where the auth callback lives. Every one of them builds the same way:

bash
go build

Cross compilation needs nothing special here. The README links an article on the subject, and a `.goreleaser.yml` at the top level is where per-platform binaries would be defined, though that file's contents are not reproduced anywhere in the documentation. What the toolchain must satisfy is written in `go.mod`:

go
module github.com/pion/turn/v5
go 1.24.0

That single line is the upgrade floor: anything older than Go 1.24 cannot build the module. The last push to the default branch landed on 2026-09-24.

Token auth and mutual TLS share one AuthHandler

Authentication is a single callback, and both documented schemes plug into it. For tokens, the instruction is to replace the username with a token in the `AuthHandler` at `examples/turn-server/simple/main.go#L53`. A password sent by the client can be any non-empty string, as long as it matches what `GenerateAuthKey` produces at line 45 of the same file. No credential store is involved; the whole decision happens in code you own.

Mutual TLS takes the same hook. You inspect the TLS connection state inside the `AuthHandler` at `examples/mutual-tls-auth/turn-server/main.go#L31`, and the `examples/mutual-tls-auth` tree holds a complete implementation that validates client certificates and matches the certificate's Common Name to the TURN username. That certificate handling rides on `github.com/pion/dtls/v3 v3.1.9`, which `go.mod` lists as an indirect requirement, so DTLS reaches your binary through a dependency rather than a direct import.

One gap deserves naming. Long-term credentials get no FAQ entry at all, even though `lt_cred.go` and an `lt-cred-generator` example both sit in the tree.

STUN and TURN answers come out of the same listener

Asked whether it will also act as a STUN server, the project's FAQ answers with a single word: yes. Underneath, the RFC list backs that up, naming RFC 5389 for STUN, RFC 5766 for TURN relay extensions, RFC 6062 for TCP allocations and RFC 6156 for IPv6. Those last two change how many sockets a relay consumes, so they matter more to capacity planning than the STUN answer does.

A second FAQ answer shapes how you size the relay. WebRTC prioritises STUN over TURN, so a relay is the path taken when direct candidates fail, not the path every session starts on. A deployment sized as though every peer pair must be relayed will be over-provisioned, and one that expects its relay to see first contact will see less traffic than it planned for. That preference belongs to the client, and nothing in this repository overrides it.

The documentation and the repository disagree in small ways

A close reading turns up three mismatches. One badge near the top links to `pkg.go.dev/github.com/pion/turn/v5`, with the major version attached, while the Using section sends you to `godoc.org/github.com/pion/turn` without it. On a module at major version 5, that second address does not describe the code you would import. That same section claims no knowledge of Go is required, then tells you to run `go build` in an example directory and read GoDoc.

A coverage badge points at `codecov.io/gh/pion/turn/branch/master/graph/badge.svg`, naming the `master` branch, while the repository's default branch is `main`. A badge will not break a build, but it does tell a reader which directory is current and which is not. None of this is fatal; all of it is resolved by trusting `go.mod` over the prose above it.

Config files versus a Go API, and what switching costs

Deployability comes first in the project's own list of properties, paired with an instruction to stop generating config files, and that is the honest trade against a standalone relay daemon. A config-driven daemon is set up from a file and changed by editing it. This one is configured in Go: allocation limits, listeners and credential checks live in `server_config.go` and change only when your code changes. Operations claims follow: allocations and state can be created and mutated at runtime, and a single environment variable switches target platforms, though which environment variable is not named anywhere in the documentation.

Inside the same ecosystem the alternative is `pion/ice`, which the README points to as a place where `pion/turn` is used. That is a different shape: the relay code is reached through an ICE package rather than called directly, so you inherit its configuration surface instead of writing your own. Going the other way, embedding `pion/turn` drags in three sibling modules with independent major versions, `github.com/pion/stun/v4`, `github.com/pion/transport/v5` and `github.com/pion/turn/v5`, which have to move together.

Direct requirements pull `github.com/stretchr/testify v1.12.1` for tests alongside `golang.org/x/time v0.14.0` and `golang.org/x/sys v0.41.0`, and a `renovate.json` at the top level is where those pins get raised. The code is MIT licensed, with a `LICENSE` file plus `LICENSES/` and `.reuse/` directories beside it, so licence metadata is tracked per file rather than in one blob.

Editorial conclusion

pion/turn fits when the relay must live inside a Go service you already operate, since allocations and listeners become Go values instead of config file edits. It does not fit when you want a package to install and point at from a separate host, because the project ships no daemon, only examples you compile. Check three things before adopting: the Go 1.24.0 floor in go.mod, whether github.com/pion/stun/v4 and github.com/pion/transport/v5 match the major versions your build already uses, and what the three relay_address_generator files require of the operator.

Frequently asked questions

Will pion/turn work as a STUN server, not just a TURN relay?

Yes. The project's own FAQ answers that it will also act as a STUN server, and the implemented RFC list names RFC 5389 for STUN alongside RFC 5766, RFC 6062 and RFC 6156 for TURN.

How do I add token-based authentication to a pion/turn server?

Replace the username with a token in the AuthHandler at examples/turn-server/simple/main.go#L53. The password sent by the client can be any non-empty string, as long as it matches the one used by GenerateAuthKey at line 45 of the same file.

Can pion/turn authenticate clients with mutual TLS?

Yes, by checking the TLS connection state in the AuthHandler at examples/mutual-tls-auth/turn-server/main.go#L31. The mutual-tls-auth example validates client certificates and matches the certificate's Common Name to the TURN username.

Which RFCs does pion/turn implement?

RFC 5389 for STUN, RFC 5766 for TURN relay extensions, RFC 6062 for TCP allocations, and RFC 6156 for IPv6 allocations. The roadmap for larger milestones is tracked in the pion/webrtc repository instead.

What Go version and module path does pion/turn v5 require?

go.mod declares the module as github.com/pion/turn/v5 and sets go 1.24.0 as the minimum. The import path carries the major version, so code must import the v5 path rather than an unversioned one.

Will WebRTC prefer my TURN server over STUN?

No. The project's FAQ answers that WebRTC prioritises STUN over TURN, which makes a relay the fallback path when direct candidates fail rather than the first hop of every session.

Official sources

  1. Issues
  2. License: MIT
  3. pion/turn on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pion-turn.svg)](https://hysenlabs.com/projects/pion-turn)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/pion-turn.svg)](https://hysenlabs.com/projects/pion-turn)

Was this useful?