From 1381540dab4d2f96c93233d805d6cc8d191b3978 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Sat, 23 May 2026 14:11:00 -0400 Subject: [PATCH] Document GPU setup, --gpus vs --runtime=nvidia, and VRAM starvation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Transcodes/Manual-Transcoding.md | 99 ++++++++++++++++++++++++++++++-- Transcodes/README-Transcoding.md | 10 +++- 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/Transcodes/Manual-Transcoding.md b/Transcodes/Manual-Transcoding.md index 4f61db6..29d754f 100644 --- a/Transcodes/Manual-Transcoding.md +++ b/Transcodes/Manual-Transcoding.md @@ -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 diff --git a/Transcodes/README-Transcoding.md b/Transcodes/README-Transcoding.md index 5bd9eb5..437f5a6 100644 --- a/Transcodes/README-Transcoding.md +++ b/Transcodes/README-Transcoding.md @@ -5,11 +5,17 @@ at full speed. When the ramdisk fills, new sessions shift to SSD automatically without interrupting anything already playing. When pressure drops, new sessions shift back to RAM. -> **Two configuration requirements that are not obvious and were both discovered the +> **Three configuration requirements that are not obvious and were all discovered the > hard way in production.** The Docker mount must use `bind-propagation=shared` or > symlink flips are silently ignored after the first flip. The `transcoding-temp` > directory must be pre-created on the ramdisk or Emby finds the SSD version and -> routes all sessions there until restarted. Both are documented in Manual-Transcoding.md. +> routes all sessions there until restarted. GPU containers require `--gpus "device=UUID"` +> in Extra Parameters — not `--runtime=nvidia` — or the container fails to start after +> unRAID 7.2.5. Additionally: any GPU-accelerated sidecar (OCR plugins, credit detection) +> that holds VRAM and never releases it will starve Emby and Jellyfin of VRAM for +> transcoding — Jellyfin hard-fails, Emby silently falls back to CPU. Emby's startup +> NVIDIA probe is one-shot: if VRAM is exhausted when the container starts, NVIDIA stays +> disabled for the entire session. All three are documented in Manual-Transcoding.md. ---