Deploying Janus WebRTC Server on Kubernetes Like a Pro
Learn how to deploy Janus WebRTC server on Kubernetes for seamless scaling. This guide covers setup, configuration, and best practices for…

Deploying Janus on Kubernetes Like a Pro

Introduction
Imagine this: you’re tasked with deploying a Janus WebRTC server — one of the most powerful and flexible WebRTC solutions available — on Kubernetes. Naturally, you think, “There must be a guide for this, right?” But as you dig deeper, you realize no one — not even the original authors of Janus — has bothered to address this challenge. Frustrating, right?
I found myself in the same situation. I was searching everywhere for a step-by-step guide but came up empty-handed. That’s when I roped in a colleague who’s a Kubernetes guru (he might deny it, but he lives for YAML files). Together, we decided to tackle this challenge head-on. I brought my Janus expertise to the table, while he unraveled the mysteries of Kubernetes. After some serious trial and error, a few late nights, and more coffee than I’d care to admit, we finally cracked it.
This post results from our combined efforts — a complete guide to deploying Janus WebRTC on Kubernetes. Whether you’re a Kubernetes enthusiast or a Janus expert (or neither!), this guide will walk you through the process step by step. By the end, you’ll have your deployment running and understand how to scale it like a pro.
Ready to dive in? Let’s get started!
Prerequisites for Deploying Janus on Kubernetes
Before we begin, ensure we meet the following requirements:
Kubernetes Cluster:
We’ll use Minikube with the Docker driver to create a local Kubernetes cluster.
- The cluster will have 3 nodes to replicate a real-world environment.
- Make sure we have installed the helm
Envoy Gateway API:
We’ll use Envoy Gateway for traffic management to simplify routing and load balancing for Janus WebRTC.
- Start a Minikube Cluster with 3 Nodes
This sets up a local Kubernetes cluster using the Docker driver with three nodes:
minikube start --nodes=3 --driver=docker
2. Install Envoy Gateway CRDs
Add the Envoy Gateway CRDs to your Kubernetes cluster to enable Gateway API for routing traffic to the right Janus pod
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.2.4 -n envoy-gateway-system --create-namespace
These steps will prepare your local environment for deploying Janus on Kubernetes. Once done, you’ll have a multi-node cluster and Gateway API ready for further steps. Let’s move to the next step!
Action Time
Before we dive into the inevitable YAML madness, let me save you some time (and sanity). I’ve already set up a GitHub repo with all the YAML and configurations you need. So, if you’re a certified Kubernetes wizard who doesn’t need a step-by-step guide, feel free to skip the lecture and head straight to janus-on-kubernetes. The rest of us mortals will carry on here.
To keep things concise, I won’t be diving into the Janus configurations — you can find those in the GitHub repo. Instead, I’ll focus on the Kubernetes setup and a handy Python CLI tool we built to simplify generating service and UDPRoute.
What is Gateway API?
The Gateway API in Kubernetes is a more flexible and extensible way to manage networking for services. It builds on Ingress but provides additional features for more complex use cases. Here are its core components:
Gateway:
A Gateway is the entry point for network traffic into your cluster. It defines how traffic should be routed (e.g., which IPs or ports to listen on). Think of it as a load balancer or reverse proxy.
GatewayClass:
A GatewayClass defines the “type” of Gateways, specifying the controller (like NGINX, Envoy, etc.) that will manage the Gateway’s behavior. It’s like a blueprint for Gateways.
Listeners:
Listeners are part of a Gateway and define the protocols and ports the Gateway should listen on (e.g., HTTP on port 80).
HTTPRoute (or other Route types):
Routes define how specific types of traffic (like HTTP or TCP) should flow through the Gateway. For example, HTTPRoute maps incoming HTTP requests to specific Kubernetes services.
Backend:
A Backend is where the traffic ends up — usually a Kubernetes Service or other resource handling the actual workload.
Together, these components make it easier to manage complex networking setups, offering more customization and scalability compared to traditional Ingress. but mostly because it supports udp routing
Now that we have a clear mental map of the Gateway API, let’s dive into writing the YAML files, starting with the deployment and service configurations.
Deployment & Service Yaml
Here, we’re creating a StatefulSet deployment along with a headless service. This setup ensures that each Janus instance gets a dedicated and stable DNS name, such as:
janus-0.janus-service.default.svc.cluster.local
janus-1.janus-service.default.svc.cluster.local
janus-2.janus-service.default.svc.cluster.local
Using the BackendTrafficPolicy (BTP) in conjunction with StatefulSets and a headless service, each DNS will consistently route traffic to a fixed Janus instance. This ensures reliable, stable, and predictable connections, which are crucial for stateful workloads like Janus where each instance needs a dedicated, stable network identity.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: janus
namespace: default # Omit or change if not using a namespace
spec:
replicas: 2
selector:
matchLabels:
app: janus
serviceName: "janus-service" # Required for StatefulSet networking
template:
metadata:
labels:
app: janus
spec:
initContainers:
- name: init-elevated
image: busybox:latest
command: ["sh", "-c"]
args:
- |
chmod -R 777 /recordings/;
securityContext:
privileged: true
volumeMounts:
- name: janus-recordings
mountPath: /recordings/
containers:
- name: janus
# you can build your own image and push it to a registry from https://github.com/flutterjanus/JanusDocker
image: shivanshtalwar0/janus-server:latest # for x86_64 => shivanshtalwar0/janus-server:linux-amd64
resources:
requests:
memory: "256Mi" # Minimum memory the container requires
cpu: "250m" # Minimum CPU the container requires
limits:
memory: "512Mi" # Maximum memory the container can use
cpu: "500m" # Maximum CPU the container can use
volumeMounts:
- name: janus-config
mountPath: /opt/janus/etc/janus
- name: janus-recordings
mountPath: /recordings
volumes:
- name: janus-config
configMap:
name: janus-config
- name: janus-recordings
hostPath:
path: /recordings
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: janus-recordings
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 100Gi
We use a service_template.yaml
along with a port-ranger.py to dynamically create the final service_generated.yaml
. This allows us to specify a custom range of ports, which is crucial for Janus as it requires UDP ports for media traversal, specifically for handling RTP media stream traffic.
# service_template.yaml
apiVersion: v1
kind: Service
metadata:
name: janus-service
namespace: default
spec:
selector:
app: janus
ports:
- name: janus-http
protocol: TCP
port: 8088
targetPort: 8088
- name: janus-ws
protocol: TCP
port: 8188
targetPort: 8188
- name: janus-a-http
protocol: TCP
port: 7088
targetPort: 7088
- name: janus-a-ws
protocol: TCP
port: 7188
targetPort: 7188
clusterIP: None # Makes the service headless
We run the command below to create service_generated.yaml
python3 port-ranger.py ./service_template.yaml janus 10000 11000 UDP ./service_generated.yaml
similarly, we will create one for UDPRoute from udpRoute_template.yaml which will come in handy later
# udpRoute_template.yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: UDPRoute
metadata:
name: janus-udp-route
namespace: default
spec:
parentRefs:
- name: envoy-gateway
namespace: default
rules:
- backendRefs:
- name: janus-service
namespace: default
port: 10000
kind: Service
# Note: UDP routes cannot exceed 16 due to Kubernetes' limitation on UDP routes
python3 port-ranger.py ./udpRoutes_template.yaml janus 10000 11000 UDP ./udp_routes_generated.yaml --chunk-size 16
Now that we’ve completed the generated YAML step, we can define the critical Gateway components. Here, we’ll outline the YAML files for the Gateway, GatewayClass, and HTTPRoute. Once we define these, we’ll be done with the core setup. Below are the YAML files for all three components:
Gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: envoy-gateway
spec:
gatewayClassName: gateway-class
listeners:
- name: janus-http-api
port: 80
hostname: "*.example.local"
protocol: HTTP
GatewayClass.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: gateway-class
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
httpRoute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend
spec:
parentRefs: [ {name: envoy-gateway, port: 80, sectionName: janus-http-api } ]
hostnames: ["*.example.local"]
rules:
- matches:
- path: {type: PathPrefix, value: /rest}
backendRefs:
- {group: "", kind: Service, name: janus-service, port: 8088}
- matches:
- path: {type: PathPrefix, value: /admin-rest}
backendRefs:
- {group: "", kind: Service, name: janus-service, port: 7088}
- matches:
- path: {type: PathPrefix, value: /admin-ws}
backendRefs:
- {group: "", kind: Service, name: janus-service, port: 7188}
- matches:
- path: {type: PathPrefix, value: /ws}
backendRefs:
- {group: "", kind: Service, name: janus-service, port: 8188}
Initialize Janus Configuration
Run the following script to initialize the Janus configuration in the ConfigMap:
./update_conf.sh
Apply Kubernetes YAML Configurations
Apply the configurations in the following order to finalize the setup:
kubectl apply -f gatewayClass.yaml
kubectl apply -f gateway.yaml
kubectl apply -f btp.yaml
kubectl apply -f httpRoutes.yaml
kubectl apply -f udp_routes_generated.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service_generated.yaml
Testing the Deployment
Update Local DNS
Update your /etc/hosts
file with the following entries:
# /etc/hosts
127.0.0.1 janus-0.example.local
127.0.0.1 janus-1.example.local
127.0.0.1 janus-2.example.local
Verify Access
Once everything is correctly configured, you can verify the Janus server’s accessibility via the following endpoints:
REST API
curl http://janus-0.example.local/rest/janus/info
WebSocket
http://janus-0.example.local/ws
Admin WebSocket
http://janus-0.example.local/admin-ws
Notes:
- Ensure the port range used in your configuration is consistent.
- For large-scale deployments, adjust the port range and user calculations as needed.
Following these steps will ensure you have a fully deployed and functional Janus setup on Kubernetes!
Conclusion
In this guide, we’ve walked through the process of deploying Janus on Kubernetes, from setting up the configurations to testing the deployment. We’ve covered essential components like StatefulSets, headless services, Gateway API, and port management to ensure a seamless deployment for Janus.
If you found this guide helpful, give it a clap! Your support helps keep this content going and encourages more technical deep dives. Happy scaling with Janus on Kubernetes!
Comments ()