Is it true that for the agent to join the room and...
# ask-ai
f
Is it true that for the agent to join the room and use webrtc it needs to be able to discover it's own public ip address. And it will try to do so via STUN
t
Thanks for reaching out, Heidi. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — good question. You're asking whether an agent must discover its own public IP via STUN in order to join a LiveKit room and use WebRTC. Short answer: No — the agent does not need to run STUN to discover its public IP. Why: • In LiveKit, an agent typically runs as a server-side participant that connects to the LiveKit server over the signaling channel. The LiveKit server (SFU) handles media routing and the WebRTC connectivity details. • The LiveKit SDKs and server manage ICE candidate gathering and NAT traversal on behalf of participants, so the agent itself doesn't need to perform STUN discovery like a peer-to-peer browser client would. • Clients use STUN/TURN when they need direct peer-to-peer connectivity or to discover a public-facing candidate, but an agent that sends/receives media via the LiveKit server relies on the server infrastructure instead. If you want, I can: • Explain how an agent should authenticate and connect to the LiveKit server (signaling steps). • Show which SDK calls are used for creating the server-side participant connection. • Describe when TURN would still be needed in your topology (for example if you run your own media routers behind restrictive NATs). Which of these would be most helpful? Sources: Agent dispatch | LiveKit Documentation | Introduction | LiveKit Documentation | OpenAI and LiveKit partner to turn Advanced Voice into an API
f
i have a problem that although the websocket signalling works between livekit-server and the agent backend (both self deployed) the rtc does not work because the backend is advertising a kubernetes internal ip address.
t
Thanks — that description helps. You're saying signaling via WebSocket succeeds, but the agent advertises a Kubernetes internal IP in its ICE candidates/SDP, so RTC can't connect. That's a common Kubernetes NAT/traversal issue and there are a few practical ways to fix it. Below I’ll outline the most likely causes and the targeted actions you can take next. What’s happening (short): • The agent inside the pod is gathering ICE candidates that include the pod’s internal cluster IP. • Those internal candidates get sent to the LiveKit server and then to peers, which cannot reach that internal address, so media fails even though signaling works. Options to fix this (pick whichever fits your topology): 1) Expose the agent with a public endpoint and advertise that address • Expose the agent through a Service type LoadBalancer, NodePort with external IP, or an ingress that provides a public hostname/IP. • Configure the agent to use that external IP/hostname when registering or connecting so it advertises reachable candidates. 2) Force media relaying through TURN (quick workaround) • Configure connections to use relay-only ICE via
iceTransportPolicy: 'relay'
so all media flows via a TURN server. This avoids relying on host-reachable candidates. • Ensure your TURN server is publicly reachable and properly configured. 3) Configure LiveKit / agent to replace private addresses with an external IP • On the LiveKit server (self-hosted) set the external IP and port range so SDPs use the public address. For example via Helm/values or server flags: set
external_ip
(or equivalent) and `port_range_start`/`port_range_end`. • If the agent has an option to override local interface detection, set it to the public IP/DNS. 4) Use host networking / adjust pod networking (if acceptable) • If security/policies permit, run the agent Pod with
hostNetwork: true
so it advertises the node’s IP rather than the pod CIDR. • Alternatively, set an environment variable or config in the agent to explicitly set the advertised address. 5) Ensure STUN/TURN are correctly configured • Make sure STUN/TURN servers are reachable and included in the agent/LiveKit rtc config so ICE provides useful public candidates or returns to TURN fallback. Diagnostic checks I recommend now 1. Capture ICE candidates or the SDP from the agent and confirm the advertised addresses (internal vs public). 2. Confirm how LiveKit server and agent are exposed (both inside k8s? LiveKit behind LB?). 3. Check whether a TURN server is configured and reachable. 4. Share the agent Pod spec networking bits (Service type, any hostNetwork setting) and the agent/LiveKit rtc config (external IP or cong flags) if you want specific config lines. If you tell me: • Is the LiveKit server also in the same Kubernetes cluster? How is it exposed (LoadBalancer/NodePort/ingress)? • Is the agent the stock livekit-agent or custom code? Which SDK/language? • Do you have a TURN server configured? I’ll give exact config examples (Kubernetes Service, Helm values, or agent config) tailored to your setup. Sources: Client SDKs | livekit/livekit_composite | DeepWiki | Agents (Python) | livekit/livekit_composite | DeepWiki | Firewall Tips for LiveKit Connectivity | LiveKit | Running LiveKit on AWS | examples/other | livekit/livekit_composite | DeepWiki
f
I am tryng to use the livekit embedded turn server for STUN in livekit config file i have turn: enabled: true udp_port: 3478 external_tls: false
t
Thanks — that config snippet is a good start. I’ll help you verify what’s needed to make the embedded TURN server act as STUN/TURN for your Kubernetes deployment and troubleshoot why clients are seeing the pod’s internal IP. Quick summary of what to check and do: • The embedded TURN server can act as a STUN/TURN endpoint, but you must ensure it is reachable from outside the cluster and the LiveKit server advertises the correct external address/ports. • Enabling
turn.enabled: true
and
udp_port: 3478
is fine, but there are additional points to verify in your LiveKit and k8s configuration. Checklist and actionable steps 1) Expose the TURN/STUN ports externally • Ensure the TURN UDP port (3478) and any TCP fallback ports are exposed by a Service (LoadBalancer or NodePort) or an ingress/NLB that forwards UDP/TCP to the LiveKit pods. • TURN uses UDP (and sometimes TCP/TLS). A simple Service must include the UDP port mapping for 3478. Example Service snippet:
Copy code
apiVersion: v1
kind: Service
metadata:
  name: livekit
spec:
  type: LoadBalancer
  ports:
    - name: stun-udp
      protocol: UDP
      port: 3478
      targetPort: 3478
    - name: stun-tcp
      protocol: TCP
      port: 3478
      targetPort: 3478
    # ensure RTP port range is exposed too (e.g., 50000-60000 UDP)
  selector:
    app: livekit
2) Make sure LiveKit advertises a public external IP/DNS • Configure LiveKit’s rtc external IP/name so SDP and ICE candidates reflect a reachable address instead of cluster-internal IPs. In Helm values or the server config set the external IP/DNS and port range (example keys are
rtc.external_ip
/
rtc.port_range_start
/
rtc.port_range_end
depending on your chart/version). • When the server knows its public IP, the SDP returned to clients will include reachable candidates. 3) Ensure RTP port range is exposed and configured • LiveKit needs a port range open for RTP/RTCP (e.g., 50000–60000). Make sure these UDP ports are accessible externally and the server’s config matches that range. 4) STUN vs TURN behavior • STUN is used to discover public candidates; TURN is used to relay media when direct connectivity fails. • If your goal is only to have correct public candidates in SDP, STUN will help clients discover their public mapping; but in Kubernetes, the LiveKit server should be the entity advertising the public-facing candidates (so configuring
external_ip
is key). • If direct connectivity is not possible, ensure TURN works as a relay fallback. Verify TURN authentication and that the TURN server returns relay candidates. 5) Verify connectivity and candidates • From an external host, try STUN binding to your exposed address: use tools like
stunclient
or
coturn
client utilities to confirm the server responds and returns a public mapped address. • Inspect the ICE candidates in browser devtools or agent logs to confirm whether public or private candidate IPs are being published. Questions that will help me give exact steps • Is LiveKit server itself running in the same Kubernetes cluster? How is it exposed to the internet (LoadBalancer, NodePort, ingress)? • Are you using the LiveKit Helm chart or a custom manifest? If Helm, which values have you set for rtc/external_ip and port range? • Do you see any TURN-related logs in LiveKit indicating the embedded TURN started successfully? • Are you attempting to use STUN (binding) only, or also trying to use TURN relay? Do you have
external_tls
false intentionally (i.e., no TLS on TURN)? If you share the LiveKit helm values (or the relevant sections of your config file) and how the Service/LoadBalancer is set up, I’ll provide exact example values and the minimal Service/Ingress manifest you should apply.