Skip to content

nginx: route /turn (REST) and /webrtc/signaling/ (WS) to selkies#153

Open
DL6ER wants to merge 2 commits intolinuxserver:masterfrom
DL6ER:fix/nginx-webrtc-signaling-and-turn
Open

nginx: route /turn (REST) and /webrtc/signaling/ (WS) to selkies#153
DL6ER wants to merge 2 commits intolinuxserver:masterfrom
DL6ER:fix/nginx-webrtc-signaling-and-turn

Conversation

@DL6ER
Copy link
Copy Markdown

@DL6ER DL6ER commented Apr 25, 2026

linuxserver.io


  • I have read the contributing guideline and understand that I have made the correct modifications

Description:

Add two missing nginx locations:

  • SUBFOLDERturn (HTTP) — proxies GET /turn to the selkies signaling server, which returns the ICE configuration JSON.
  • ^/webrtc/signaling/?$ (regex, WebSocket-upgrade) — proxies the WebRTC-mode signaling URL to the same selkies process.

Done in both the :3000 and the :3001 server blocks of root/defaults/default.conf.

Benefits of this PR and context:

The selkies dashboard issues two requests during initialisation that currently fall through to nginx's default 404 handler:

  1. fetch('./turn') — the dashboard expects the JSON response defined in selkies/signaling_server.py (if path == "/turn": ...). With no nginx route, the browser receives an HTML 404 page and the dashboard logs Uncaught (in promise) SyntaxError: Unexpected token '<', "<html>...".
  2. new WebSocket('wss://.../webrtc/signaling/') — built by selkies-wr-core.js (new URL(protocol + window.location.host + pathname + appName + "/signaling/"), with appName === "webrtc"). The selkies signaling server upgrades any path ending in /signaling/ but the bundled nginx config has no matching location, so the browser sees NS_ERROR_WEBSOCKET_CONNECTION_REFUSED.

With these two locations in place, both SELKIES_MODE=webrtc and dual mode work without any container-side patching.

How Has This Been Tested?

Built locally on top of master (debian-trixie), composed an debian-xfce-style downstream image, ran with:

docker run -d \
  -p 3010:3000 \
  -e PUID=1000 -e PGID=1000 -e TZ=Europe/Berlin \
  -e SELKIES_MODE=webrtc \
  -e SELKIES_ENABLE_DUAL_MODE=true \
  -e SELKIES_TURN_HOST=... -e SELKIES_TURN_PORT=3478 \
  -e SELKIES_TURN_PROTOCOL=udp -e SELKIES_TURN_SHARED_SECRET=... \
  webtop:patched

Validated with:

$ curl -i https://<host>/turn
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
{ "lifetimeDuration": "86400s", "iceServers": [ ... ] }

WebSocket upgrade for the WebRTC signaling URL returns 101:

$ curl -i -H 'Upgrade: websocket' -H 'Connection: Upgrade' \
       -H 'Sec-WebSocket-Key: <16 random bytes b64>' \
       -H 'Sec-WebSocket-Version: 13' \
       https://<host>/webrtc/signaling/
HTTP/1.1 101 Switching Protocols

Browser end-to-end: dashboard initialises, peer connection establishes, H.264 video and Opus audio render in Firefox 149 + Chromium 142.

Source / References:

  • TURN endpoint handler: src/selkies/signaling_server.py, the branch if path == "/turn/" or path == "/turn": in process_request.
  • WebRTC signaling path construction: addons/selkies-web-core/selkies-wr-core.js upstream, the new URL(protocol + window.location.host + pathname + appName + "/signaling/") call with appName === "webrtc" from selkies-dashboard's WebRTC mode.

The selkies signaling-server exposes two endpoints that the frontend
needs but that the bundled nginx config does not currently route:

- `GET /turn` returns the WebRTC ICE config (TURN/STUN servers + HMAC
  credentials) as JSON. The dashboard fetches this on init.
- `wss://.../webrtc/signaling/` is the WebSocket upgrade endpoint used
  in WebRTC mode (selkies-wr-core builds the URL as
  `${pathname}<appName>/signaling/`, the dashboard sets `appName=webrtc`).

Without these locations the dashboard logs:
- a `JSON.parse: unexpected character` when /turn returns nginx's HTML
  404 page,
- `NS_ERROR_WEBSOCKET_CONNECTION_REFUSED` on the signaling URL,
followed by a stuck loading state.

Both endpoints are served by the same selkies process listening on
127.0.0.1:CWS, so the proxy_pass target is unchanged from the existing
/websocket location.

Tested with `SELKIES_MODE=webrtc` and `SELKIES_ENABLE_DUAL_MODE=true`:
fetching /turn returns the expected RTC config JSON, and the WebRTC
peer connection is established.
Copilot AI review requested due to automatic review settings April 25, 2026 15:58
@LinuxServer-CI LinuxServer-CI moved this to PRs Ready For Team Review in Issue & PR Tracker Apr 25, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds missing nginx routes so Selkies’ signaling server endpoints are reachable through the bundled reverse proxy configuration (both HTTP :3000 and HTTPS :3001 server blocks).

Changes:

  • Add an HTTP location to proxy SUBFOLDERturn to the Selkies signaling server.
  • Add a WebSocket-upgrade location intended to proxy /webrtc/signaling/ to the Selkies signaling server.
  • Duplicate these locations in both nginx server blocks (ports 3000 and 3001).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread root/defaults/default.conf
Comment thread root/defaults/default.conf Outdated
Comment thread root/defaults/default.conf
Comment thread root/defaults/default.conf Outdated
…UBFOLDER

Two issues raised in review (both server blocks, :3000 and :3001):

1. The signaling regex was anchored at root: `^/webrtc/signaling/?$`. With
   a non-default SUBFOLDER deployment (e.g. SUBFOLDER=/foo/), the request
   path `/foo/webrtc/signaling/` would not match and would fall through
   to the static handler. Replace the leading `/` with the SUBFOLDER
   placeholder so the same sed substitution that processes every other
   location block also applies here. Default `SUBFOLDER=/` keeps the
   regex equivalent to before.

2. The new WebSocket location was missing the long timeouts and
   forwarded client headers that `SUBFOLDERwebsocket` already sets.
   Without them, signaling sessions can be cut by nginx defaults and
   selkies upstream sees only 127.0.0.1 as the client IP. Mirror the
   full set: X-Real-IP, X-Forwarded-For, X-Forwarded-Proto, the three
   3600s timeouts, and client_max_body_size 10M. /turn gains the same
   forwarded headers (no timeouts — short HTTP request).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds missing nginx routes so Selkies’ signaling server endpoints used by the dashboard don’t fall through to the default 404 handler (notably /turn for ICE config and /webrtc/signaling/ for WebRTC-mode signaling).

Changes:

  • Add an HTTP proxy location for SUBFOLDERturn to forward /turn to the Selkies signaling server.
  • Add a WebSocket-upgrade regex location for SUBFOLDERwebrtc/signaling/?$ to forward WebRTC signaling to the same backend.
  • Apply both routes in the :3000 (http) and :3001 (https) nginx server blocks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@LinuxServer-CI
Copy link
Copy Markdown
Collaborator

I am a bot, here are the test results for this PR:
https://ci-tests.linuxserver.io/lspipepr/selkies-base/debiantrixie-aa141909-pkg-aa141909-dev-c49d8ea8912005f81c67e95e37753dfcec831699-pr-153/index.html
https://ci-tests.linuxserver.io/lspipepr/selkies-base/debiantrixie-aa141909-pkg-aa141909-dev-c49d8ea8912005f81c67e95e37753dfcec831699-pr-153/shellcheck-result.xml

Tag Passed
amd64-debiantrixie-aa141909-pkg-aa141909-dev-c49d8ea8912005f81c67e95e37753dfcec831699-pr-153
arm64v8-debiantrixie-aa141909-pkg-aa141909-dev-c49d8ea8912005f81c67e95e37753dfcec831699-pr-153

@LinuxServer-CI
Copy link
Copy Markdown
Collaborator

I am a bot, here are the test results for this PR:
https://ci-tests.linuxserver.io/lspipepr/selkies-base/debiantrixie-aa141909-pkg-aa141909-dev-112435d0b4e800d99bf4b06ade1f9c88e744a83e-pr-153/index.html
https://ci-tests.linuxserver.io/lspipepr/selkies-base/debiantrixie-aa141909-pkg-aa141909-dev-112435d0b4e800d99bf4b06ade1f9c88e744a83e-pr-153/shellcheck-result.xml

Tag Passed
amd64-debiantrixie-aa141909-pkg-aa141909-dev-112435d0b4e800d99bf4b06ade1f9c88e744a83e-pr-153
arm64v8-debiantrixie-aa141909-pkg-aa141909-dev-112435d0b4e800d99bf4b06ade1f9c88e744a83e-pr-153

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: PRs Ready For Team Review

Development

Successfully merging this pull request may close these issues.

3 participants