From a3ab90bd8ba8dc0fe008740bf125f2d01ac37a0a Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Thu, 8 Oct 2026 19:58:27 -0400 Subject: [PATCH 1/4] docs: add emulator streaming troubleshooting page Distills setup issues from the Discord streaming thread: standalone container smoke test, NVIDIA GPU passthrough, silent crashes, remote access and reverse proxying, ROM path mismatches, duplicate YAML keys and browser gamepad support. Moves the existing troubleshooting list off the feature page. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/Navigation.md | 1 + docs/troubleshooting/emulator-streaming.md | 97 ++++++++++++++++++++++ docs/troubleshooting/index.md | 4 + docs/using/emulator-streaming.md | 7 +- 4 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 docs/troubleshooting/emulator-streaming.md diff --git a/docs/Navigation.md b/docs/Navigation.md index 9ef24d70..c2d8ef62 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -111,6 +111,7 @@ search: - [Authentication](troubleshooting/authentication.md) - [In-Browser Play](troubleshooting/in-browser-play.md) - [Netplay](troubleshooting/netplay.md) + - [Emulator Streaming](troubleshooting/emulator-streaming.md) - [Synology](troubleshooting/synology.md) - [Kubernetes](troubleshooting/kubernetes.md) - About diff --git a/docs/troubleshooting/emulator-streaming.md b/docs/troubleshooting/emulator-streaming.md new file mode 100644 index 00000000..314c523c --- /dev/null +++ b/docs/troubleshooting/emulator-streaming.md @@ -0,0 +1,97 @@ +--- +title: Emulator Streaming Troubleshooting +description: Fix emulator streaming setup, GPU and network issues +--- + +# Emulator Streaming Troubleshooting + +Most of this applies to both the [webstation](../using/emulator-streaming.md) container and the deprecated per-emulator broker mods, and where they differ it's called out. + +## Test the container on its own first + +Get the emulator container working by itself before you add it to `config.yml`. Open its Selkies web UI from the machine you'll play on, launch a game from inside the emulator, and check that it runs at full speed. If it doesn't, nothing in RomM's config will fix it. + +To confirm the GPU is in use, open a terminal inside the container and run `vkcube`. It should name your graphics card, and `llvmpipe` means the container fell back to software rendering. When a desktop container can't get the GPU at all, smoke-test a plain Selkies container such as [webtop](https://docs.linuxserver.io/images/docker-webtop/) with the [LinuxServer GPU guide](https://docs.linuxserver.io/selkies/user-guide/gpu/) before you go back to the emulator. + +## The GPU isn't used, or the emulator fails to initialize + +On NVIDIA, these are the only GPU settings a Selkies container needs: + +```yaml +shm_size: 1gb +devices: + - /dev/nvidia-modeset:/dev/nvidia-modeset +deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [compute, video, graphics, utility] +``` + +On a standard Linux host, Compose only picks this up once NVIDIA is Docker's default runtime: + +```bash +sudo nvidia-ctk runtime configure --runtime=docker +sudo systemctl restart docker +``` + +Start from a minimal compose file with only the image, the broker variables, ports, volumes and the block above. Extra settings such as `security_opt`, `QT_QPA_PLATFORM` or `PIXELFLUX_WAYLAND` can stop the emulator from starting, so add them back one at a time once it works. + +If Dolphin fails to initialize its video backend on OpenGL, switch it to Vulkan (`GFXBackend = Vulkan` under `[Core]` in `Dolphin.ini`, in the container's `/config` volume). + +## The container crashes without an error + +Check the host's kernel log right after the crash. The first command shows NVIDIA driver (Xid) errors, and the second shows processes the kernel killed for running out of memory: + +```bash +dmesg -T | grep -i xid +dmesg -T | grep -i "killed process" +``` + +If both are empty, look for session and connection errors in the container's own log: + +```bash +docker logs --since 10m | grep -i -E "timeout|idle|heartbeat|disconnect|session" +``` + +## No stream action on a platform + +Either `streaming.enabled` is off, nothing is configured for that platform slug, or the config hasn't reloaded. The streaming config is read when the app loads, so refresh the page after editing `config.yml`. + +A YAML mapping can't hold the same key twice, so a second `containers:` under `streaming:` replaces the first and only the containers in the last one show up. Put every entry under a single `containers:` list. + +## The stream never loads, or fails outside your home network + +The stream is an iframe the browser loads straight from `host`, so `host` has to be HTTPS and reachable from wherever the browser is. A LAN address works at home but gives a 502 or a blank player outside it. Open the `host` URL directly from the client machine to see what it does. + +For remote play, put the container behind your [reverse proxy](../install/reverse-proxy.md) and set `host` to its public HTTPS address. Selkies serves a self-signed certificate on its HTTPS port, so either proxy to that port with certificate checks turned off (`tls_insecure_skip_verify` in Caddy) or proxy to the container's plain HTTP port. + +`broker_host` is only called by the RomM server, so it can stay on an internal address such as `http://pcsx2:8000` when both containers share a Docker network. With the per-emulator broker mods, the broker listens on its own port (`8000` by default) apart from the Selkies stream (`3001`). Exposing the broker separately takes a second proxy host pointing at that port. A webstation container serves the broker under its `SUBFOLDER` on the same origin as the stream, so a single proxy host covers both. + +## Launch or save errors out + +Either the server can't reach `broker_host` or the secret is wrong. Check that `STREAMING_BROKER_SECRET` matches the container's `BROKER_SECRET`, and that `broker_host` resolves from the RomM server. A `broker_secret` in `config.yml` is ignored whenever `STREAMING_BROKER_SECRET` is set (see [Set the shared secret](../using/emulator-streaming.md#set-the-shared-secret)). + +## The emulator can't find the game + +The path sent to the container is `library_path` (default `/romm/library`) followed by the ROM's path inside the RomM library, such as `roms/ps2/game.iso`. The container's mount has to mirror the RomM library layout under that prefix. If your library uses `roms/ps2/` and you mount the PS2 folder at `/romm/library/ps2/roms`, the emulator gets a path that doesn't exist. Either fix the mount or set `library_path` to wherever the container sees the library. + +Dolphin also remembers the game directories it was given. If it keeps looking in an old folder after you change the mount, remove the stale entries from its game list and add the correct directory again. + +## Controllers or the virtual gamepad don't respond + +Try another browser before anything else, because some don't pass gamepad input through to Selkies. Zen has failed on both Linux and macOS, while Firefox and Vivaldi work. + +## A container shows as unconfigured + +Its `host` is missing a scheme, or there's no reachable broker for it, and it can't be claimed until that's fixed. + +## A platform is stuck as in use + +Someone disconnected without releasing it. Wait for the heartbeat to go stale or force-release it from the fleet (see [Emulator Streaming](../using/emulator-streaming.md)). + +## "The previous session is still saving" + +An exit is still pulling state off the container, so give it a moment and try again. If saves on a slow container time out, raise `STREAMING_SAVE_TIMEOUT` (see [Environment Variables → Emulator Streaming](../reference/environment-variables.md#emulator-streaming)). diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index 3908cc75..7382463f 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -35,6 +35,10 @@ description: Diagnose common issues by symptom - EmulatorJS won't load/404 → [In-Browser Play Troubleshooting](in-browser-play.md) (on the slim image, cores come from a CDN at runtime, so a 404 usually means outbound networks are blocked). - Netplay doesn't connect → [Netplay Troubleshooting](netplay.md) (the cause is almost always NAT or missing ICE servers) +## Emulator streaming + +- Stream won't load, launch fails or the GPU isn't used → [Emulator Streaming Troubleshooting](emulator-streaming.md) + ## Platform-specific - [Synology Troubleshooting](synology.md): permission errors, DSM gotchas diff --git a/docs/using/emulator-streaming.md b/docs/using/emulator-streaming.md index 848c8691..a574c448 100644 --- a/docs/using/emulator-streaming.md +++ b/docs/using/emulator-streaming.md @@ -189,9 +189,4 @@ Three env vars bound how long streaming waits, listed with their defaults in [En ## Troubleshooting -- **No stream action on a platform.** Either `streaming.enabled` is off, nothing is configured for that platform slug, or the config hasn't reloaded. RomM reads the streaming config when the app loads, so refresh the page after editing `config.yml`. -- **Stream never loads.** The browser can't reach `host`, or `host` isn't HTTPS. Open the Selkies URL directly in a browser from the client machine and see what happens. -- **Launch or save errors out.** Either the server can't reach `broker_host` or the secret is wrong. Check `STREAMING_BROKER_SECRET` matches on both sides, and that `broker_host` actually resolves from the server. -- **A container shows as unconfigured in the fleet.** Its `host` is missing a scheme, or RomM has no reachable broker for it. It can't be claimed until that's fixed. -- **Platform stuck as in use.** Someone disconnected without releasing it. Wait for the heartbeat to go stale or force-release it from the fleet. -- **"The previous session is still saving".** An exit is still pulling state off the container. Give it a moment and try again. +Setup, GPU, reverse proxy and session issues are covered in [Emulator Streaming Troubleshooting](../troubleshooting/emulator-streaming.md). From 2072c3f4510395b374ccbc138279d89b64603c21 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Thu, 8 Oct 2026 20:10:02 -0400 Subject: [PATCH 2/4] docs: drop per-emulator broker mod content from streaming troubleshooting Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/troubleshooting/emulator-streaming.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/troubleshooting/emulator-streaming.md b/docs/troubleshooting/emulator-streaming.md index 314c523c..2c4fa763 100644 --- a/docs/troubleshooting/emulator-streaming.md +++ b/docs/troubleshooting/emulator-streaming.md @@ -5,11 +5,9 @@ description: Fix emulator streaming setup, GPU and network issues # Emulator Streaming Troubleshooting -Most of this applies to both the [webstation](../using/emulator-streaming.md) container and the deprecated per-emulator broker mods, and where they differ it's called out. - ## Test the container on its own first -Get the emulator container working by itself before you add it to `config.yml`. Open its Selkies web UI from the machine you'll play on, launch a game from inside the emulator, and check that it runs at full speed. If it doesn't, nothing in RomM's config will fix it. +Get the [webstation](../using/emulator-streaming.md#run-the-webstation-container) container working by itself before you add it to `config.yml`. Open its Selkies web UI from the machine you'll play on, launch a game from inside the emulator, and check that it runs at full speed. If it doesn't, nothing in RomM's config will fix it. To confirm the GPU is in use, open a terminal inside the container and run `vkcube`. It should name your graphics card, and `llvmpipe` means the container fell back to software rendering. When a desktop container can't get the GPU at all, smoke-test a plain Selkies container such as [webtop](https://docs.linuxserver.io/images/docker-webtop/) with the [LinuxServer GPU guide](https://docs.linuxserver.io/selkies/user-guide/gpu/) before you go back to the emulator. @@ -39,8 +37,6 @@ sudo systemctl restart docker Start from a minimal compose file with only the image, the broker variables, ports, volumes and the block above. Extra settings such as `security_opt`, `QT_QPA_PLATFORM` or `PIXELFLUX_WAYLAND` can stop the emulator from starting, so add them back one at a time once it works. -If Dolphin fails to initialize its video backend on OpenGL, switch it to Vulkan (`GFXBackend = Vulkan` under `[Core]` in `Dolphin.ini`, in the container's `/config` volume). - ## The container crashes without an error Check the host's kernel log right after the crash. The first command shows NVIDIA driver (Xid) errors, and the second shows processes the kernel killed for running out of memory: @@ -68,7 +64,7 @@ The stream is an iframe the browser loads straight from `host`, so `host` has to For remote play, put the container behind your [reverse proxy](../install/reverse-proxy.md) and set `host` to its public HTTPS address. Selkies serves a self-signed certificate on its HTTPS port, so either proxy to that port with certificate checks turned off (`tls_insecure_skip_verify` in Caddy) or proxy to the container's plain HTTP port. -`broker_host` is only called by the RomM server, so it can stay on an internal address such as `http://pcsx2:8000` when both containers share a Docker network. With the per-emulator broker mods, the broker listens on its own port (`8000` by default) apart from the Selkies stream (`3001`). Exposing the broker separately takes a second proxy host pointing at that port. A webstation container serves the broker under its `SUBFOLDER` on the same origin as the stream, so a single proxy host covers both. +The broker is served under the container's `SUBFOLDER` on the same origin as the stream, so a single proxy host covers both. `broker_host` is only called by the RomM server, so if you set it, it can stay on an internal address. ## Launch or save errors out @@ -78,8 +74,6 @@ Either the server can't reach `broker_host` or the secret is wrong. Check that ` The path sent to the container is `library_path` (default `/romm/library`) followed by the ROM's path inside the RomM library, such as `roms/ps2/game.iso`. The container's mount has to mirror the RomM library layout under that prefix. If your library uses `roms/ps2/` and you mount the PS2 folder at `/romm/library/ps2/roms`, the emulator gets a path that doesn't exist. Either fix the mount or set `library_path` to wherever the container sees the library. -Dolphin also remembers the game directories it was given. If it keeps looking in an old folder after you change the mount, remove the stale entries from its game list and add the correct directory again. - ## Controllers or the virtual gamepad don't respond Try another browser before anything else, because some don't pass gamepad input through to Selkies. Zen has failed on both Linux and macOS, while Firefox and Vivaldi work. From fa6721e840d2014577901c3657e57a2cd1a2f3f3 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 10 Oct 2026 11:29:32 -0400 Subject: [PATCH 3/4] docs: drop Selkies HTTPS port from streaming proxy advice Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/troubleshooting/emulator-streaming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/troubleshooting/emulator-streaming.md b/docs/troubleshooting/emulator-streaming.md index 2c4fa763..a8704464 100644 --- a/docs/troubleshooting/emulator-streaming.md +++ b/docs/troubleshooting/emulator-streaming.md @@ -62,7 +62,7 @@ A YAML mapping can't hold the same key twice, so a second `containers:` under `s The stream is an iframe the browser loads straight from `host`, so `host` has to be HTTPS and reachable from wherever the browser is. A LAN address works at home but gives a 502 or a blank player outside it. Open the `host` URL directly from the client machine to see what it does. -For remote play, put the container behind your [reverse proxy](../install/reverse-proxy.md) and set `host` to its public HTTPS address. Selkies serves a self-signed certificate on its HTTPS port, so either proxy to that port with certificate checks turned off (`tls_insecure_skip_verify` in Caddy) or proxy to the container's plain HTTP port. +For remote play, put the container behind your [reverse proxy](../install/reverse-proxy.md) and set `host` to its public HTTPS address. Point the proxy at the container's plain HTTP port and let the proxy handle TLS. The broker is served under the container's `SUBFOLDER` on the same origin as the stream, so a single proxy host covers both. `broker_host` is only called by the RomM server, so if you set it, it can stay on an internal address. From a7f1a5e1e79442aa5084ce1a76cf914cdf01985c Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 10 Oct 2026 11:30:32 -0400 Subject: [PATCH 4/4] docs: point streaming host at a reverse proxy, not the Selkies HTTPS port Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/using/emulator-streaming.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using/emulator-streaming.md b/docs/using/emulator-streaming.md index a574c448..49ed5314 100644 --- a/docs/using/emulator-streaming.md +++ b/docs/using/emulator-streaming.md @@ -157,7 +157,7 @@ streaming: enabled: true containers: - protocol: webstation - host: https://192.168.1.56:3010 + host: https://stream.example.com subfolder: /streaming label: Emulation station platforms: @@ -170,7 +170,7 @@ streaming: Four of those keys have consequences worth knowing before you pick their values: -- `host` is what the browser connects to, and it has to be **HTTPS**: Selkies WebRTC won't run without a secure context. Use the container's self-signed cert, or put it behind a [reverse proxy with TLS](../install/reverse-proxy.md). A path like `/streaming` works if you've proxied the container onto RomM's own origin, but then you must set `broker_host` yourself, because a bare path gives RomM no address to call. +- `host` is what the browser connects to, and it has to be **HTTPS**: Selkies WebRTC won't run without a secure context. Put the container's plain HTTP port behind a [reverse proxy with TLS](../install/reverse-proxy.md). A path like `/streaming` works if you've proxied the container onto RomM's own origin, but then you must set `broker_host` yourself, because a bare path gives RomM no address to call. - `broker_host` is only ever called server to server, so plain HTTP is fine. Pooled containers are identified by it, so two serving the same platform need different ones. - `library_path` is where the container sees your library. Don't change it casually, since your state and save history is keyed to it. - The per-platform `emulator` names what that platform's states and memory cards are filed under, so renaming it later orphans everything stored under the old name. A container-level `emulator` is ignored whenever `platforms` is used.