Document GPU setup, --gpus vs --runtime=nvidia, and VRAM starvation

Required Mount section now shows full Extra Parameters for both GPU and
non-GPU containers and explains why --gpus beats --runtime=nvidia at
setup time rather than burying it in Troubleshooting.

New troubleshooting entry documents the GPU memory starvation cascade:
OCR sidecar (PaddleOCR) holds VRAM → Jellyfin hard-fails, Emby falls
back to CPU silently. Emby's NVIDIA startup probe is one-shot — VRAM
must be free before restarting or NVIDIA stays disabled for the session.
Includes correct recovery sequence and Tesseract fallback for EmbyCredits.

README callout updated from two to three non-obvious requirements.
This commit is contained in:
Gmer4Lfe
2026-05-23 14:11:00 -04:00
parent 767e64c072
commit 1381540dab
2 changed files with 103 additions and 6 deletions
+95 -4
View File
@@ -44,16 +44,43 @@ suppressed.
### Required Mount
In Emby's **Extra Parameters** in the unRAID Docker template:
Everything goes in the unRAID Docker template **Extra Parameters** field. Do NOT use the
path mapping UI for the transcode directory — it does not support `bind-propagation`.
**Non-GPU containers:**
```
--mount type=bind,source=/mnt/ram-transcode,target=/ext-ram-transcode,bind-propagation=shared
```
This **replaces** the transcode path in the standard template path mapping UI. Do NOT
add this via the path mapping UI — that UI does not support propagation. Extra Parameters only.
**GPU-accelerated containers (Emby, Jellyfin with NVENC/NVDEC):**
```
--gpus "device=GPU-62e1659d-1ed4-935f-3df3-4bb4339438f1" --pids-limit=0 --mount type=bind,source=/mnt/ram-transcode,target=/ext-ram-transcode,bind-propagation=shared
```
In Emby's transcoding settings, set the transcode path to `/ext-ram-transcode`.
Replace the UUID with your GPU's UUID (`nvidia-smi -L` to find it).
In Emby and Jellyfin's transcoding settings, set the transcode path to `/ext-ram-transcode`.
---
### `--gpus` vs `--runtime=nvidia`
There are two ways to give a container GPU access. **Always use `--gpus`.**
`--runtime=nvidia` is the old approach. It requires the NVIDIA Container Toolkit configured
at the Docker daemon level and spreads across multiple XML fields (runtime flag +
`NVIDIA_VISIBLE_DEVICES` env var + `NVIDIA_DRIVER_CAPABILITIES` env var). When unRAID
rebuilds a container from template (update, reinstall) these fields can break or get dropped
— requiring manual XML repair to recover. `--runtime=nvidia` combined with
`NVIDIA_VISIBLE_DEVICES` also conflicts with `bind-propagation=shared` on unRAID 7.2.5+
due to a kernel change in mount namespace initialization (see
[Troubleshooting](#emby-with-nvidia-gpu--bind-propagationshared-fails-to-start) for the
full error and fix).
`--gpus "device=UUID"` is Docker-native GPU support (Docker 19.03+). One field in Extra
Parameters. Pins a specific GPU by UUID — no ambiguity on a single-GPU system. Survives
container rebuilds cleanly. Does not conflict with `bind-propagation=shared`.
**HOST1 GPU UUID (Quadro P2000):** `GPU-62e1659d-1ed4-935f-3df3-4bb4339438f1`
### Why `shared` Is Required
@@ -482,6 +509,70 @@ leave the container in a broken state where Emby refuses to start at all.
# Re-adding the mount after a clean start works reliably.
```
### GPU Memory Exhausted — Jellyfin Fails, Emby Falls Back to CPU
**Symptoms:**
- Jellyfin refuses to play anything — session fails immediately, no transcode starts
- Emby plays but transcodes on CPU (logs show `libx265` or `libx264` instead of `h264_nvenc`/`hevc_nvenc`)
Both symptoms can appear at the same time and have the same root cause: another process has
consumed all available VRAM and not released it. The media servers respond differently to a
failed GPU session init:
- **Jellyfin:** hard fails — no session is created, playback stops entirely
- **Emby:** falls back to CPU transcoding silently and keeps going
```bash
# Confirm VRAM is exhausted:
nvidia-smi
# Find what is holding it:
nvidia-smi --query-compute-apps=pid,used_memory,name --format=csv,noheader
```
**Common source — OCR plugin sidecars:** Credit detection and subtitle extraction plugins
often talk to a GPU-accelerated OCR container running alongside the media server. The
EmbyCredits plugin (`yocksers/EmbyCredits`) can be configured to use a PaddleOCR backend.
PaddleOCR loads a neural network into VRAM at first use and **does not release it between
runs**. After a single credit scan the GPU memory stays consumed, starving Emby and
Jellyfin of VRAM for transcoding.
This is not obvious because PaddleOCR is a separate container — `nvidia-smi` shows the
process, but the connection to failing playback is not immediate.
**Emby startup probe — the restart trap:**
Emby runs a one-shot NVIDIA hardware detection when the container starts. If VRAM is
exhausted at startup, NVIDIA is marked unavailable for the entire container session — there
is no retry. Emby will use CPU for all transcoding until the container is restarted, and
only after VRAM has been freed. Restarting Emby while VRAM is still exhausted causes the
probe to fail again and NVIDIA is disabled again.
Correct recovery sequence:
1. Free VRAM (stop the offending container/process)
2. Confirm VRAM is free: `nvidia-smi` — GPU memory used should drop to near zero
3. Restart Emby — startup probe now succeeds, NVENC available
**Fix — switch OCR to CPU:**
For EmbyCredits: use the `yock1/embycreditocr` Tesseract image instead of PaddleOCR.
Tesseract is CPU-based, never touches VRAM, and is the plugin's own documented backend.
```bash
docker run -d \
--name EmbyCredit-OCR \
-p 8884:8884 \
--restart unless-stopped \
yock1/embycreditocr
```
In EmbyCredits plugin settings, set the OCR endpoint to `http://localhost:8884`.
The accuracy tradeoff is real (PaddleOCR is stronger on non-Latin scripts) but GPU
starvation is not an acceptable failure mode for a live media server.
---
### Emby With NVIDIA GPU — bind-propagation=shared Fails to Start
> **Regression:** This worked before unRAID 7.2.5. The kernel update changed how mount