TURN server
coturn
Kubernetes deployment
cloud infrastructure
Kubernetes networking

How to deploy TURN servercoturn inside Kubernetes

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Running Coturn in Kubernetes is possible, but TURN servers are more network-sensitive than ordinary HTTP services. The hard part is not the container image. The hard part is exposing the right external IP and relay port range so clients can actually negotiate media through the server.

Understand the Network Requirements First

A TURN server normally needs:

  • the listener port, commonly 3478 for UDP and TCP
  • optional TLS port, often 5349
  • a relay port range for media traffic
  • a stable public IP that clients can reach

This is where Kubernetes complicates things. TURN relay traffic is not confined to one small service port. Coturn allocates relay ports dynamically, so if you keep the default huge port range, the Kubernetes service definition becomes awkward very quickly.

That is why most real deployments do one of two things:

  • restrict Coturn to a smaller relay port range
  • use hostNetwork or another direct-networking approach

A Minimal Coturn Configuration

A practical turnserver.conf for Kubernetes usually includes a narrowed relay range and explicit external identity:

conf
1listening-port=3478
2fingerprint
3use-auth-secret
4static-auth-secret=replace-me
5realm=turn.example.com
6external-ip=203.0.113.10
7min-port=49160
8max-port=49200

The external-ip value matters because WebRTC clients need the correct public address in the ICE candidates. If Coturn advertises the wrong IP, connectivity fails even though the pod is running.

Deploy the Pod and Expose the Ports

A simplified deployment might look like this:

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: coturn
5spec:
6  replicas: 1
7  selector:
8    matchLabels:
9      app: coturn
10  template:
11    metadata:
12      labels:
13        app: coturn
14    spec:
15      containers:
16        - name: coturn
17          image: coturn/coturn:latest
18          args: ["-c", "/etc/coturn/turnserver.conf"]
19          ports:
20            - containerPort: 3478
21              protocol: UDP
22            - containerPort: 3478
23              protocol: TCP
24          volumeMounts:
25            - name: config
26              mountPath: /etc/coturn
27      volumes:
28        - name: config
29          configMap:
30            name: coturn-config

And the service:

yaml
1apiVersion: v1
2kind: Service
3metadata:
4  name: coturn
5spec:
6  type: LoadBalancer
7  selector:
8    app: coturn
9  ports:
10    - name: turn-udp
11      port: 3478
12      targetPort: 3478
13      protocol: UDP
14    - name: turn-tcp
15      port: 3478
16      targetPort: 3478
17      protocol: TCP

This is only the control path. If you restrict the relay range, you also need that range routable through your environment. That is the part many first deployments miss.

Why hostNetwork Is Often Considered

TURN is sometimes easier on hostNetwork because the pod can bind directly to the node’s network stack, which avoids extra address translation confusion and makes relay port handling more straightforward.

That is not mandatory, but it is a legitimate choice when:

  • you need predictable public IP behavior
  • UDP relay ports must map cleanly
  • your load balancer setup is limited

Kubernetes is not fighting you here. TURN simply has different networking assumptions than a typical REST API.

Authentication and Secrets

Do not bake shared secrets directly into the image. Store them in a Kubernetes Secret and mount or inject them into the Coturn config.

For WebRTC deployments, long-term credentials with use-auth-secret are a common pattern because the application can mint short-lived TURN credentials instead of storing a permanent username and password in clients.

Common Pitfalls

The biggest mistake is exposing only port 3478 and forgetting that TURN also needs a relay port range for media traffic.

Another issue is advertising the wrong external IP. Coturn may be healthy, logs may look fine, and the service may still fail because the ICE candidates contain an unreachable address.

Developers also often treat TURN like a normal web service and hide it behind networking layers that do not handle UDP relay traffic well. TURN is far more sensitive to NAT and port mapping details.

Finally, avoid leaving the relay range wide open by default in Kubernetes. Narrow it deliberately so you can reason about firewall rules, service exposure, and observability.

Summary

  • Coturn in Kubernetes is mainly a networking problem, not a container problem.
  • Restrict the relay port range and make sure it is actually reachable.
  • Configure the correct external IP so clients get valid ICE candidates.
  • Consider hostNetwork if service-based exposure makes relay traffic too awkward.
  • Store TURN secrets in Kubernetes secrets and test with real WebRTC clients, not just pod health checks.

Course illustration
Course illustration

All Rights Reserved.