"In the livekit design, If I am using haproxy loadbalancer beforw livekit node to manafer wss sigling and turn traffic via haproxy instead directly to livekit nodes, I have done all the setup and
I deployed new Livekit setup in kubernetes , where Livekit is working with my existing deployed chat application call MMS service running os same cluster in different namespace, which is abstraction
layer for managing the token, So architecture point of view I am using client (external) --------haproxy loadbalancer (wss or signaling sni
media.example.com + WebRTC sni media-nturn .
external.com
connection on 3478 (UDP)) -------> istioingressGateway + Virtualservice, I have created two services into the istio-system for service one service called X4-ingressgateway which is recieving the all
TCPrequest + other livekit-ingressGateway service for livekit traffic, Like wss(signaling) on port 443 TCP is getting managed by livekit-istioingressgateway -------> livekit-gateway which two endpoint
one for
livekit-quartz.external.com for (WSS request) and other route
livekit-nturn-quartz.external.com (turn request on port 3478/UDP) connection with policy externalTrafficPolicy: Local or
externalTrafficPolicy: Cluster. currently livekit cluster is running and integrated by chat service,
also I am not using public IP directly NAT with livekit nodes, Istead I am reusing the Haproxy Public IP
and configure Haproxy public IP in the livekit config.yaml in RTC section in my livekit version v1.9.4 use_external_ip: true also external_ip: "Public_IP_Of _Haproxy",
- Currently I have deployed the livekit into the K8s as daemonset along with redis+Sentinel cluster.
--------- Architecture ------------------
Model A – LiveKit Behind L4 LoadBalancer (Correct)
HAProxy passes:
TCP 443 → WSS Signaling
UDP 3478 → LiveKit TURN (From haproxy --- Livekit node on 3478 direct via hostNetwork)
UDP media ports 40k - 50k → (From haproxy --- Livekit node on 40k - 50k
LiveKit advertises HAProxy public IP
BUT HAProxy must be a pure L4 passthrough
Issue:
while initiating call getting two major issue:
1. Logs MMS service Room mimatch error.
2026/02/16 20
4248.541266 app.go
997 [Warn] failed to load views: lstat /app/client/dist: no such file or directory
{"file":"/app/pkg/models/notification_scheduler.go:49","func":"mms/pkg/models.(*NtfyScheduleModel).StartNtfyScheduler","level":"info","msg":"Ntfy duration at 5h20m11.458672017s","time":"2026-02-16T20
4248Z"}
┌───────────────────────────────────────────────────┐
│ media server version: 1.0.0 │
│ Fiber v2.52.9 │
│
http://127.0.0.1:8080 │
│ (bound on host 0.0.0.0 and port 8080) │
│ │
│ Handlers ........... 135 Processes ........... 1 │
│ Prefork ....... Disabled PID ................ 16 │
└───────────────────────────────────────────────────┘
20
4258 | 401 | 19.879935ms | 127.0.0.6 | POST | /_mms/v2/auth/notification/room | -
20
4258 | 401 | 4.464213ms | 127.0.0.6 | POST | /_mms/auth/room/isRoomActive | -
Engine initialization SUCCESS
olm initialize using new context: memory manager = 0x7f6d1bc578b0, threading manager = 0x7f6d1bc57910, logging_provider = 0, crypto_systems = 0x7f6d1bc5cb20
Signature_valid is ::1
{"file":"/app/pkg/models/room_service.go:63","func":"mms/pkg/models.(*RoomService).LoadRoomInfo","level":"info","msg":"Requested room does not exist for the given roomId: !TArEBZroIjLSfNszvi:
quartz1.service421.com","time":"2026-02-16T20
4258Z"}
{"file":"/app/pkg/models/room_service.go:302","func":"mms/pkg/models.(*RoomService).LoadRoomWithMetadata","level":"info","msg":"load room info error from server for the given roomId: !TArEBZroIjLSfNszvi:
quartz1.service421.com","time":"2026-02-16T20
4258Z"}
{"file":"/app/pkg/models/room_auth.go:163","func":"mms/pkg/models.(*RoomAuthModel).IsRoomActive","level":"info","msg":"The room information: requested room does not exist","time":"2026-02-16T20
4258Z"}
{"file":"/app/pkg/models/room_service.go:63","func":"mms/pkg/models.(*RoomService).LoadRoomInfo","level":"info","msg":"Requested room does not exist for the given roomId: !TArEBZroIjLSfNszvi:
quartz1.service421.com","time":"2026-02-16T20
4258Z"}
{"file":"/app/pkg/models/room_service.go:63","func":"mms/pkg/models.(*RoomService).LoadRoomInfo","level":"info","msg":"Requested room does not exist for the given roomId: !TArEBZroIjLSfNszvi:
quartz1.service421.com","time":"2026-02-16T20
4258Z"}
20
4258 | 200 | 64.27182ms | 127.0.0.6 | POST | /_mms/auth/room/isRoomActive | -
olm is already initialized: memory manager = 0x7f6d1bc578b0, threading manager = 0x7f6d1bc57910, logging_provider = 0, crypto_systems = 0x7f6d1bc5cb20
Signature_valid is ::1
{"file":"/app/pkg/models/room_service.go:63","func":"mms/pkg/models.(*RoomService).LoadRoomInfo","level":"info","msg":"Requested room does not exist for the given roomId: !TArEBZroIjLSfNszvi:
quartz1.service421.com","time":"2026-02-16T20
4258Z"}
{"file":"/app/pkg/models/room_service.go:302","func":"mms/pkg/models.(*RoomService).LoadRoomWithMetadata","level":"info","msg":"load room info error from server for the given roomId: !TArEBZroIjLSfNszvi:
quartz1.service421.com","time":"2026-02-16T20
4258Z"}
{"file":"/app/pkg/models/room_auth.go:163","func":"mms/pkg/models.(*RoomAuthModel).IsRoomActive","level":"info","msg":"The room information: requested room does not exist","time":"2026-02-16T20
4258Z"}
----------------
http://livekit.livekit.svc.cluster.local:7880 --- IynL2Bc7m6lChivgG3f1mZe7xAbtvp2avhq ------ cgXMxCM5TzmpVAqMLjkt63Uhdinp5VqKu
20
4258 | 200 | 80.986171ms | 127.0.0.6 | POST | /_mms/v2/auth/room/create | -
olm is already initialized: memory manager = 0x7f6d1bc578b0, threading manager = 0x7f6d1bc57910, logging_provider = 0, crypto_systems = 0x7f6d1bc5cb20
2. Livekit is unable to advertize the ICE candidiates, I mean client is not getting livekit Server address along with dynamic port between 40k - 50k.
--------- Architecture ------------------
Model A – LiveKit Behind L4 LoadBalancer (Correct)
HAProxy passes:
TCP 443 → LiveKit
UDP 3478 → LiveKit TURN
UDP media ports → LiveKit nodes
LiveKit advertises HAProxy public IP
BUT HAProxy must be a pure L4 passthrough
Model B – Istio L7 + HAProxy (Problematic for WebRTC)
Istio is great for:
HTTP
gRPC
WSS
But WebRTC media + TURN:
Needs UDP
Needs real client IP
Needs stable port advertisement
Needs symmetric NAT awareness
Istio + HAProxy + externalTrafficPolicy misalignment = broken ICE negotiation.
-------------- hapee config (enterprise) ---------------------
# ============================================
# UDP PORT 3478 - STUN/TURN (UDP) - FIXED
# ============================================
# REMOVE TPROXY/TRANSPARENT MODE - Use standard UDP load balancing
# Option 1: Simple UDP configuration (recommended)
listen udp_livekit_turn_simple
mode udp
bind :3478
# IMPORTANT: Use source IP hashing for session affinity
balance source
# Timeouts for WebRTC (long sessions)
timeout client 4h
timeout server 4h
# Server options - use check-ssl for health checks if needed
server lk01 lk01-quartz:3478 check
server lk02 lk02-quartz:3478 check
server lk03 lk03-quartz:3478 check
# Option 2: Advanced UDP configuration with source port preservation
frontend udp_livekit_turn
mode udp
bind :3478
# Use a backend that preserves source for better NAT traversal
default_backend bk_livekit_turn_udp
backend bk_livekit_turn_udp
mode udp
balance source
# Force source IP preservation (critical for ICE)
source 0.0.0.0 usesrc clientip
# UDP specific settings
option log-health-checks
# Adjust these based on your network latency
timeout check 5s
timeout client 4h
timeout server 4h
# LiveKit TURN servers on UDP 3478
server lk01 lk01-quartz:3478 check
server lk02 lk02-quartz:3478 check
server lk03 lk03-quartz:3478 check
# ============================================
# UDP PORTS 50000-60000 - TURN Relay Ports
# ============================================
# This is for allocating relay candidates
listen udp_livekit_relay
mode udp
bind :50000-60000
# Source affinity is critical here
balance source
timeout client 4h
timeout server 4h
# Each LiveKit node should have its own relay port range
# Adjust port mapping based on your LiveKit configuration
server lk01 lk01-quartz:50000-60000 check
server lk02 lk02-quartz:50000-60000 check
server lk03 lk03-quartz:50000-60000 check
# ============================================
# HTTP Health Checks
# ============================================
frontend livekit_health
bind *:8080
mode http
acl is_health path /health
use_backend bk_health if is_health
default_backend bk_default_http
backend bk_health
mode http
option httpchk GET /livekit/health
http-check expect status 200
server lk01 lk01-quartz:7881 check
server lk02 lk02-quartz:7881 check
server lk03 lk03-quartz:7881 check
backend bk_default_http
mode http
server placeholder 127.0.0.1:80 disabled
# ============================================
# Stats endpoint
# ============================================
frontend stats
bind *:1936
mode http
stats enable
stats uri /haproxy?stats
stats refresh 10s
stats show-legends
no log
livekit firewall rules opened in the Palo Alto
---------------------------------------------------
Rule -1 Opend
3478/UDP & 40000 - 50000 UDP
Internet ------------------------------------------> haproxy public IP
Rule -2 opend
443/TCP 7880/TCP 5349/TCP
Haproxy ------------------------------------------> livekit worker node
Rule -3 opened
7880 & 7881 TCP
livekit worker node ------------------------------- > livekit worker node
Do I need to open the below rules also
Rule -4 opend ?
40000 - 50000 UDP
Haproxy ------------------------------------------> livekit worker node