STUNner: running your own STUN and TURN inside a Kubernetes cluster
A Kubernetes media gateway for WebRTC. Contact: [email protected]
At a glance
- What is it?
- STUNner is a Go implementation of a STUN and TURN gateway for WebRTC deployments in Kubernetes, and it exists because the default way to get clients behind a NAT onto a media server is a public STUN server or a hosted TURN service. It offers two deployment models, a headless one and a media plane one, speaks the Kubernetes Gateway API, and ships as a scratch based container image.
- Who is it for?
- Adopt STUNner if you run WebRTC media servers in Kubernetes and you want NAT traversal to be part of your own infrastructure rather than a public STUN server or a hosted TURN provider, and if you can operate a Kubernetes gateway. Do not adopt it if your media servers are outside Kubernetes, since the whole design is tied to cluster networking and to the Gateway API..
- 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 last received commits 1 day ago.
- 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two shapes: a NAT traversal server or a media plane front door
STUNner is described as a Kubernetes media gateway for WebRTC, and the main goal in the README is to allow anyone to deploy their own WebRTC infrastructure into Kubernetes without relying on any external service other than the cloud provider's standard hosted Kubernetes offering. The architecture section makes the two options explicit. In the headless model, STUNner acts as a standalone STUN and TURN server that WebRTC clients and media servers can use as a scalable NAT traversal facility. In the media plane model, it acts as a gateway for ingesting WebRTC media traffic into the Kubernetes cluster by exposing a public facing STUN and TURN server that clients connect to. The difference is where the media stops. In the headless model, STUNner is a facility that exists alongside your media servers, and in the media plane model it is the public door, with the media servers behind it inside the cluster. The README's claim about the outcome is that this makes it possible to deploy WebRTC application servers and media servers into ordinary Kubernetes pods, and to take advantage of the full cloud native feature set to manage, scale, monitor and troubleshoot the WebRTC infrastructure like any other Kubernetes workload.
The problem is the port range and the public dependency
The README spends most of its opening on the problem, and the problem is worth understanding on its own terms. WebRTC, the README says, lacks a virtualization story, which is why there is no easy way to deploy a WebRTC media service into Kubernetes and still benefit from the resiliency, scalability and high availability features people expect from modern network services. The specific difficulty is Kubernetes container networking and the surprising ways it interacts with UDP and RTP media, which the README illustrates with a series of Stack Overflow questions about load balancers opening a wide range of thousands of ports, using WebRTC with RTCPeerConnection on Kubernetes, and scaling video conferencing software in Kubernetes. The second half of the problem is a dependency. The entire industry relies on a handful of public STUN servers and hosted TURN services to connect clients behind a NAT or firewall, and the README lists what that creates: a useless dependency on externally operated services, a performance bottleneck, security concerns and a non-trivial price tag. STUNner is the answer to both halves, and the reason it is built as a Kubernetes workload with the Gateway API is that the port range problem is a cluster problem.
Standards compliance is the feature that keeps your client unchanged
Two properties matter for anyone evaluating this. The first is that STUNner exposes a standards compliant STUN and TURN gateway, which means clients keep full browser compatibility and your existing WebRTC codebase needs minimal or no modification. For a media product that is the whole ballgame, because the alternative to a self hosted TURN service is either changing the client code, changing the ICE configuration in every browser session, or paying a provider. The second is that STUNner supports the Kubernetes Gateway API, so it can be configured in the familiar YAML engineering style via Kubernetes manifests rather than through a bespoke configuration format of its own. That is a bigger deal than it sounds for a project whose whole problem statement is that Kubernetes networking is confusing. A configuration surface that looks like the rest of your cluster manifests is the difference between a tool your platform team can review and one they will ask you to justify. The implementation is in Go, the module is `github.com/l7mp/stunner/v2`, and the dependency list shows the media stack is built on the Pion libraries, with pion/webrtc, pion/turn, pion/ice, pion/dtls, pion/datachannel and pion/transport in the require list.
Three binaries and a container with nothing in it
The build produces three executables, which tells you what the tool expects to be used for. The Makefile builds `stunnerd`, `stunnerctl` and `icetester` into the `bin/` directory, with the version, commit hash and build date injected through ldflags and the paths trimmed. `stunnerd` is the daemon, `stunnerctl` is the control client, and `icetester` is a connectivity test tool, which is the piece you reach for when a client cannot get media through and you need to separate a gateway problem from a session problem. The container image is built with a `golang:1.27-alpine` builder that cross compiles with `CGO_ENABLED=0`, and the runtime stage is `FROM scratch`, so the image contains the binary and a configuration file and nothing else. The exposed port is 3478 over UDP, which is the standard STUN port, and the default command runs the daemon against a bundled configuration file:
EXPOSE 3478/udp
CMD [ "stunnerd", "-c", "/stunnerd.conf" ]The Makefile also has a `generate` target that runs the OpenAPI code generation for the configuration package, and the test and build targets both depend on `generate`, `fmt` and `vet`, so the generated client and server code cannot drift from the schema. That is why `kin-openapi` and the oapi-codegen tooling appear in the dependencies, and it is the reason the configuration API is a documented HTTP interface rather than a set of flags.
Configuration, reconciliation and the control plane
The repository layout shows a project that treats configuration as data rather than as flags. There is a `config.go` with its test, a `reconcile.go` with its test, `tls.go`, `tools.go` and a `stunner.go` at the root, alongside `internal/`, `pkg/` and `cmd/` directories, a `deploy/` directory for the Kubernetes manifests, `docs/` for the documentation, a `perf/` directory and a `bin/` directory. The Kubernetes client libraries are all present, with `k8s.io/api`, `k8s.io/apimachinery`, `k8s.io/cli-runtime` and `k8s.io/client-go` at version 0.37.1, which is what lets the daemon read and reconcile against the cluster it runs in, and `sigs.k8s.io/yaml` for the manifest side. The HTTP surface uses gorilla/mux for routing and gorilla/websocket for the live connection, and the metrics and traces go through OpenTelemetry with the Prometheus exporter and the Prometheus client, so a STUNner deployment fits into an existing observability stack rather than asking for a separate one. The API is described by an OpenAPI document and the client and server are generated from it, which means an operator can read the configuration surface as a specification rather than as source code.
Testing, benchmarks and the shape of the releases
The test suite in the root is telling, because the names tell you what the authors thought could break. There is an auth test, a filter test, a plain listener test, a server unix test, a status test and a turn relay test, which maps onto the failure modes of a STUN and TURN server: authentication of the long term credentials, the filtering of who may use the relay, the handling of plain listeners for clients that cannot use the native transport, the server's own lifecycle on Unix, the status reporting, and the relay path itself. Alongside them there is a `benchmark.sh` and a `perf/` directory, and the project publishes coverage through Coveralls with a tests workflow badge, plus a golangci-lint configuration and a lint target. The release history is described in terms of milestones rather than features, with v1.1.0 titled as the second stable release, upgrading STUNner to carrier grade performance, and v1.2.0 as the third stable release, with improvements for production use, followed by v1.2.1 in June 2026. The repository also carries MILESTONES.org, a SECURITY policy, a code of conduct and an AUTHORS file, and the README warns that it documents the latest development version while the stable documentation is published separately, which is a distinction worth keeping in mind when you read a feature list that may describe unreleased behaviour.
Editorial conclusion
Adopt STUNner if you run WebRTC media servers in Kubernetes and you want NAT traversal to be part of your own infrastructure rather than a public STUN server or a hosted TURN provider, and if you can operate a Kubernetes gateway. Do not adopt it if your media servers are outside Kubernetes, since the whole design is tied to cluster networking and to the Gateway API.. Before you deploy, read the stable documentation rather than the README, since the README documents the latest development version while the stable docs live elsewhere, and check the Gateway API support of your cluster. The licence is MIT, the current release is 1.2.1 from 2026-06-04, and the last push was on 2026-10-14.
Frequently asked questions
What is STUNner used for?
STUNner lets you run your own STUN and TURN server for WebRTC clients inside Kubernetes, instead of depending on public STUN servers or hosted TURN services. It exposes a standards compliant gateway, so browser compatibility is kept and your existing WebRTC code needs minimal or no modification.
What are the headless and media plane deployment models?
In the headless model STUNner is a standalone STUN and TURN server that clients and media servers use as a NAT traversal facility. In the media plane model it is a gateway that ingests WebRTC media traffic into the cluster, exposing a public facing STUN and TURN server with the media servers behind it.
Which Kubernetes APIs does STUNner use?
STUNner supports the Kubernetes Gateway API, so it can be configured with familiar YAML manifests. The Go dependencies include the Kubernetes client libraries at version 0.37.1, and the repository has a reconcile.go, which points at the daemon reconciling its configuration against cluster resources.
Which binaries does the STUNner build produce?
Three: `stunnerd`, the daemon, `stunnerctl`, the control client, and `icetester`, a connectivity testing tool. The container image is built from a Go builder and runs from `scratch`, exposing 3478 over UDP, and the Makefile has a `generate` target that runs the OpenAPI code generation for the configuration package.
How is STUNner monitored?
The project depends on OpenTelemetry with its Prometheus exporter and the Prometheus client library, and there is a `benchmark.sh` script and a `perf/` directory alongside the unit tests for authentication, filtering, plain listeners, the turn relay and status reporting.
Official sources
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.
[](https://hysenlabs.com/projects/l7mp-stunner)Was this useful?