Hello everyone,
I am experiencing an issue with OpenVidu Egress when the deployment is behind NAT, and I would appreciate guidance on whether this is a supported setup or if I am missing a required configuration.
Environment
-
OS: Oracle Linux 9
-
OpenVidu version: 3.4.1 (deployed using the official installation script)
-
TURN/STUN: Caddy with
--experimental-turn-tls-with-main-domain -
Topology:
-
Server internal IP:
10.10.99.9 -
NAT / WAN IP (test setup):
192.168.32.253 -
Production has a real public IP (works fine there (no NAT) )
-
Network configuration
The following ports are forwarded via DNAT to the OpenVidu server:
-
TCP:
80,443 -
TCP/UDP:
7881 -
UDP: high port range as recommended in the OpenVidu documentation
What works
-
Clients can successfully:
-
Connect to OpenVidu
-
Create rooms
-
Join conferences
-
Publish/subscribe media
-
-
This works both:
-
From internal networks (e.g.
10.10.98.2) -
From a workstation simulating an “external” zone
-
-
Interestingly, conferencing still works even when high UDP ports are blocked
The problem: Egress
The issue appears only with Egress.
Observations
-
Egress launches Chromium inside the container and attempts to join the room
-
When the OpenVidu deployment is behind NAT, Egress never actually starts recording
-
Even with all ports fully opened, the behavior is the same
-
Network capture shows:
-
Traffic to API over
443 -
Some traffic to Pion / LiveKit over
7881 -
Then the flow breaks
-
Egress logs (excerpt)
waiting for start signal
...
egress_aborted
error: "Start signal not received"
code: 412
details: "End reason: Source closed"
Chromium starts, the GStreamer pipeline is built, but the start signal is never received, and the pipeline is eventually torn down.
OpenVidu / LiveKit side logs
On the OpenVidu side, I only see peer connection failures related to DTLS:
peer connection state changed: closed
Failed to start SCTP: DTLS not established
failed to open SrtpSession: the DTLS transport has not started yet
This strongly suggests that the Egress participant never establishes a proper WebRTC connection.
Important comparison
With the exact same configuration, when I deploy OpenVidu on a VM with a directly bound public IP (no NAT):
-
Egress works perfectly
-
Recording starts immediately
-
No DTLS / start-signal issues
This makes me believe the problem is specifically related to NAT traversal for Egress, not a general Egress bug.
Question
Is OpenVidu Egress officially supported behind NAT?
If yes:
-
Are there additional requirements for Egress (extra ports, advertised IPs, ICE/TURN settings)?
-
Does Egress require a publicly routable IP for DTLS/SRTP to complete?
If no:
- Is a public IP on the Egress/OpenVidu node a hard requirement?
Any clarification or pointers would be greatly appreciated.
Thank you in advance for your help.
And here are some raw logs:
openvidu | 2025-12-29T22:18:41.073Z DEBUG livekit analytics/analytics.go:256 events:{id:"AE_sQrRoF2iicjP" type:WEBHOOK timestamp:{seconds:1767046721 nanos:73262371} node_id:"ND_xZMCuLdsNxCB" webhook:{event_id:"EV_u2sDvBijDwCk" event:"egress_ended" egress_id:"EG_afnnZBPofPAd" created_at:{seconds:1767046721} queued_at:{seconds:1767046721 nanos:58632507} queue_duration_ns:191445 sent_at:{seconds:1767046721 nanos:58824133} send_duration_ns:14396705 url:"``http://127.0.0.1:6080/livekit/webhook``" service_status:"EGRESS_ABORTED" service_error_code:412 service_error:"Start signal not received"}}
openvidu | 2025-12-29T22:14:23.365Z INFO livekit.transport.pion.pc v4@v4.1.1/peerconnection.go:507 peer connection state changed: closed {"room": "Roomxxx-voee6epajiez7p9", "roomID": "RM_kaW4odrDoTwQ", "participant": "EG_scjWh5hgiAuZ", "pID": "PA_XVLpoJRCFTip", "remote": false, "transport": "PUBLISHER"} openvidu | 2025-12-29T22:14:23.365Z WARN livekit.transport.pion.pc v4@v4.1.1/peerconnection.go:2662Failed to start manager: connecting canceled by caller {"room": "Roomxxx-voee6epajiez7p9", "roomID": "RM_kaW4odrDoTwQ", "participant": "EG_scjWh5hgiAuZ", "pID": "PA_XVLpoJRCFTip", "remote": false, "transport": "SUBSCRIBER"} openvidu | 2025-12-29T22:14:23.365Z WARN livekit.transport.pion.pc v4@v4.1.1/peerconnection.go:1572Failed to start SCTP: DTLS not established {"room": "Roomxxx-voee6epajiez7p9", "roomID": "RM_kaW4odrDoTwQ", "participant": "EG_scjWh5hgiAuZ", "pID": "PA_XVLpoJRCFTip", "remote": false, "transport": "SUBSCRIBER"} openvidu | 2025-12-29T22:14:23.365Z WARN livekit.transport.pion.pc v4@v4.1.1/peerconnection.go:1859undeclaredMediaProcessor failed to open SrtpSession: the DTLS transport has not started yet {"room": "Roomxxx-voee6epajiez7p9", "roomID": "RM_kaW4odrDoTwQ", "participant": "EG_scjWh5hgiAuZ", "pID": "PA_XVLpoJRCFTip", "remote": false, "transport": "SUBSCRIBER"} openvidu | 2025-12-29T22:14:23.365Z WARN livekit.transport.pion.pc v4@v4.1.1/peerconnection.go:1931undeclaredMediaProcessor failed to open SrtcpSession: the DTLS transport has not started yet {"room": "Roomxxx-voee6epajiez7p9", "roomID": "RM_kaW4odrDoTwQ", "participant": "EG_scjWh5hgiAuZ", "pID": "PA_XVLpoJRCFTip", "remote": false, "transport": "SUBSCRIBER"}
openvidu | 2025-12-29T21:57:49.782Z INFO livekit.webhook webhook/resource_url_notifier.go:295 sent webhook {"event": "egress_ended", "id": "EV_7opZTWgvvvdH", "webhookTime": 1767045469, "url": "``http://127.0.0.1:6080/livekit/webhook``", "egressID": "EG_qCmDdaT3xY6v", "status": "EGRESS_ABORTED", "error": "Start signal not received", "queueDuration": "69.263µs", "sendDuration": "12.313141ms"} openvidu | 2025-12-29T21:57:54.378Z INFO livekit.api service/twirp.go:128 API Egress.ListEgress {"service": "Egress", "method": "ListEgress", "room": "Room-msisq5ityceq30l", "duration": "258.89µs", "status": "200"}
egress | 2025-12-29T21:57:34.406Z DEBUG egress source/web.go:218 launching chrome {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v", "url": "``http://localhost:7980/?layout=grid&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjcxMzE4NTQsImlzcyI6IkFQSTNkY25KaTIzaWdCTCIsImtpbmQiOiJlZ3Jlc3MiLCJuYmYiOjE3NjcwNDU0NTQsInN1YiI6IkVHX3FDbURkYVQzeFk2diIsInZpZGVvIjp7ImNhblB1Ymxpc2giOmZhbHNlLCJjYW5QdWJsaXNoRGF0YSI6ZmFsc2UsImNhblN1YnNjcmliZSI6dHJ1ZSwiaGlkZGVuIjp0cnVlLCJyZWNvcmRlciI6dHJ1ZSwicm9vbSI6IlJvb20tbXNpc3E1aXR5Y2VxMzBsIiwicm9vbUpvaW4iOnRydWV9fQ.aNXWOE3vFtAicgKBwgLxRGtyU3J0O6QVc9h17Hhkzxw&url=ws%3A%2F%2F127.0.0.1%3A7880``", "sandbox": false, "insecure": false} egress | 2025-12-29T21:57:34.666Z DEBUG egress source/web.go:341 chrome initialized {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:34.719Z DEBUG egress gstreamer/bin.go:70 adding src audio to pipeline {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:34.721Z DEBUG egress gstreamer/bin.go:70 adding src video to pipeline {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:34.722Z DEBUG egress gstreamer/bin.go:76 adding sink file to pipeline {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:34.728Z DEBUG egress pipeline/controller.go:181 waiting for start signal{"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.743Z INFO egress source/web.go:320 chrome: END_RECORDING {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.743Z DEBUG egress pipeline/controller.go:353 stopping pipeline {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v", "reason": "Source closed"} egress | 2025-12-29T21:57:49.743Z DEBUG egress gstreamer/state.go:75 pipeline state building -> stopping {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.743Z DEBUG egress source/web.go:121 closing chrome {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.743Z DEBUG egress gstreamer/state.go:75 pipeline state stopping -> finished {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.763Z DEBUG egress source/web.go:130 closing X display {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.765Z DEBUG egress source/web.go:136 unloading pulse module {"nodeID": "NE_W8GPLiWCgwvg", "handlerID": "EGH_NrpBf84UYi5u", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v"} egress | 2025-12-29T21:57:49.769Z INFO egress info/io.go:230 egress_aborted {"nodeID": "NE_W8GPLiWCgwvg", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v", "requestType": "room_composite", "outputType": "file", "error": "Start signal not received", "code": 412, "details": "End reason: Source closed"} egress | 2025-12-29T21:57:49.770Z DEBUG egress server/server_rpc.go:172 egress metrics {"nodeID": "NE_W8GPLiWCgwvg", "clusterID": "", "egressID": "EG_qCmDdaT3xY6v", "avgCPU": 0.08539135044934755, "maxCPU": 0.6934673366849723, "maxMemory": 1205219328}

