Getting Started

Install Media Preview Generator with Docker or Docker Compose, then set up NVIDIA, Intel or AMD GPU acceleration, Unraid, volume mounts and networking.

Start with your setup

Choose the instructions that apply

These choices point to the existing instructions below. They do not change or generate a container command.

  1. Check prerequisites
  2. Run the container
  3. Get the access token
  4. Complete the setup wizard
  5. Check server settings
  6. Add webhooks or a schedule

Generate preview thumbnails for Plex, Emby, Jellyfin, or any combination of them.

Important

This page is the source of truth for installation and first-time setup. For operations and troubleshooting, use Guides & Troubleshooting. For exact settings and API contracts, use Configuration & API Reference.

On this page

Prerequisites

  1. At least one media server reachable from this container — any of:
    • Plex Media Server — sign in with your Plex account in the Setup Wizard, or enter a server URL + token if you can't reach plex.tv
    • Emby Server — server URL + username and password, or an API key
    • Jellyfin Server — use Quick Connect (a code you enter in Jellyfin, no password to type), or a username and password, or an API key
  2. Docker installed on your server

The container can run on the same machine as your media server or on another one. If the best GPU is in a different machine, run the app anywhere and add that machine as a worker node.

You can configure several servers, even a mix of vendors. One FFmpeg pass publishes to every server that owns the file.


Quick Start (Docker)

Step 1: Run the Container

docker run -d \
  --name media-preview-generator \
  --restart unless-stopped \
  -p 8080:8080 \
  --device /dev/dri:/dev/dri \
  -e PUID=1000 \
  -e PGID=1000 \
  -v /path/to/media:/media:ro \
  -v /path/to/plex/config:/plex:rw \
  -v /path/to/app/config:/config:rw \
  -v /etc/localtime:/etc/localtime:ro \
  stevezzau/media_preview_generator:latest

Replace /path/to/media, /path/to/plex/config, and /path/to/app/config with your actual paths.

Important

This command is for Plex, which only writes into /plex, so the media can stay :ro. Using Emby, or Jellyfin? Change the media mount to :rw, because those previews are written next to each video. (Jellyfin's off-media mode is the exception.) See Volume Mounts.

Note

No environment variables are required for first-time setup. Server connections (Plex / Emby / Jellyfin), libraries, workers, and path mappings are all configured in the Setup Wizard, Servers and Settings. Environment variables are optional overrides (see Reference).

Tip

Timezone: The /etc/localtime mount makes log timestamps and scheduled jobs use your local time. If your host doesn't have this file (e.g. some NAS devices), use -e TZ=America/New_York instead (replace with your timezone).

Step 2: Get Your Access Token

Find your token using the Authentication Token section below.

Step 3: Complete the Setup Wizard

  1. Open http://YOUR_SERVER_IP:8080
  2. Enter the authentication token
  3. Follow the wizard. Its steps are Server type, Connect, Paths, Options and Security:
    • Step 1 — Choose your media server: click Plex, Emby or Jellyfin.
      • Plex → Sign in with Plex, then Next. If plex.tv is blocked, open Can't reach plex.tv? Enter server details manually, enter the URL and token, and click Verify connection.
      • Emby → enter the server URL, then choose Username + Password or API Key. Click Test connection, then Save server.
      • Jellyfin → enter the server URL, then choose Quick Connect, Username + Password or API Key. With Quick Connect, click Start Quick Connect and enter the code it shows in your Jellyfin profile menu. Then click Test connection and Save server.
      • Skip for now ends the wizard and takes you to Servers, where you can add servers later.
    • Step 2 — Connect your server (Plex only): pick the Plex server, then tick the libraries to monitor. Tick none to monitor all. Emby and Jellyfin skip this step. Their libraries come in when you save the server, and you manage them later in Servers → Edit → Libraries.
    • Step 3 — Where are your files?: for Plex, confirm the Plex Media Server data folder (usually /plex in this container). Use Check a real file to confirm this container can read a media file at the path your server reports. Under Advanced, add path mappings and exclude paths if you need them.
    • Step 4 — Processing options: GPU device tuning lists detected GPUs (Re-scan GPUs re-detects them). Below it, set up your worker groups, then the Thumbnails interval and quality. These are shared by every configured server.
    • Step 5 — Set your access token: choose a new token (at least 8 characters, different from the current one), then click Complete Setup. If WEB_AUTH_TOKEN is set, the token is fixed and this step says so.

You can add more servers, any vendor and any number of each, at any time with Servers → Add Server. A file present on multiple servers is generated once and published to each in its native format. To go through the wizard again, use Tools → Run setup again.


This app's main job is video preview thumbnails, the small frames you see when you drag the scrub bar. Plex stores them as BIF bundles, Emby reads a BIF sidecar next to the media file, and Jellyfin reads a folder of JPG tile sheets called trickplay next to the media file. The app writes the right format for each. Optional CPU generation of Plex chapter thumbnails is also available for Plex 1.43.4.x or 1.43.5.x, off by default. It can also send Skip Intro / Skip Credits markers to your servers, off until you turn it on per server (see the Intro & Credits guide).

Turn off built-in preview generation on each server you configure so this tool isn't competing with a redundant CPU job:

The Setup Health tab (Servers → Edit) checks all of these and can toggle them for you.

Tip

After setup, you probably want one or both of:

  • Radarr/Sonarr webhooks — auto-process new imports.
  • A daily cron schedule (0 2 * * *) on the Automation page — catches anything the webhooks miss.

Volume Mounts

Container Path Purpose Mode Required when…
/media Your media files. FFmpeg reads the videos from here. ro if only Plex is configured. rw if Emby is configured, or Jellyfin in its default layout. Always.
/plex Plex application data (where Plex stores BIF bundles) rw Plex is configured — BIFs land inside Plex's config at Media/localhost/<hash>/.../index-sd.bif. Omit this mount entirely if you don't configure any Plex server.
/config This app's settings, schedules, and job history rw Always

Which servers need the media mounted read-write:

With a :ro media mount, Emby and default-layout Jellyfin can't save anything. Each file fails for that server with "Could not write preview file: … Read-only file system" in the job's per-server result and the logs. Those files aren't retried. Other servers on the same file are unaffected. No setup check warns about this in advance.

Note

Jellyfin and Emby don't need Plex's config mount. Emby BIFs and default-layout Jellyfin tiles are written next to each media file under /media, and the scan-nudge is sent over HTTP. So a Jellyfin-only or Emby-only setup needs /media (read-write) + /config. Jellyfin's off-media mode needs Jellyfin's config folder (read-write) as well. A mixed setup needs whichever mount(s) each configured server requires.


Authentication Token

Use this section whenever documentation asks for your authentication token.

On first start the app makes a random token and saves it in auth.json, in the folder you mounted as /config. The logs show only its last 4 characters, so read it from that file:

docker exec media-preview-generator cat /config/auth.json

You can also set a fixed token for predictable logins:

WEB_AUTH_TOKEN=your-password

Docker Compose

See docker-compose.example.yml for ready-to-use configurations:

Configuration Use Case
GPU Single service covering Intel, AMD, and NVIDIA with inline comments
CPU-Only Minimal service; disable all GPUs in Settings after first run
Unraid Intel iGPU default with NVIDIA alternative, Unraid paths and permissions

Copy the file, uncomment the section for your hardware, and adjust volume paths.

Warning

Don't set init: true. This container manages its own processes internally; init: true conflicts with that and prevents the container from starting.


GPU Acceleration

Hardware-accelerated video processing for faster thumbnail generation. To use a GPU in another machine, add it as a worker node. To check what's detected on your system, open the web UI (http://YOUR_IP:8080) and go to Settings → Processing options → GPU device tuning. Detected GPUs are listed there with device IDs, names, and types. The setup wizard shows the same list in step 4.

Supported GPUs

GPU Type Platform Acceleration Docker Support
NVIDIA Linux CUDA/NVENC NVIDIA Container Toolkit
AMD Linux VAAPI Device passthrough
Intel Linux VAAPI/QuickSync Device passthrough
NVIDIA Windows CUDA/NVENC Docker Desktop (WSL2 backend) + --gpus all
AMD/Intel Windows — Not available — CPU only
Apple Silicon / Intel macOS — Not available — CPU only

Note

NVIDIA on Windows works under Docker. The NVIDIA Windows driver exposes CUDA and NVDEC into WSL2, so Docker Desktop using the WSL2 backend accelerates much like Linux does. Follow the NVIDIA GPU steps below — you do not need to install anything inside WSL. Treat it as best-effort: GPU detection under WSL2 is less reliable than on a native Linux host.

Note

AMD/Intel on Windows and all GPUs on macOS cannot be accelerated under Docker. Docker Desktop runs a Linux VM, and D3D11VA (Windows AMD/Intel) and VideoToolbox (macOS) are host-OS frameworks that the VM cannot reach. Those setups process on CPU — add CPU workers in Settings → Workers to compensate. If you need GPU acceleration, run the container on a Linux host. Apple Silicon users still benefit from the native ARM64 image (no Rosetta overhead).

Intel iGPU (QuickSync)

Most common setup, especially on Unraid.

docker run -d \
  --device /dev/dri:/dev/dri \
  -e PUID=1000 \
  -e PGID=1000 \
  stevezzau/media_preview_generator:latest

Verify the device exists:

ls -la /dev/dri
# Should show: card0, renderD128

The container auto-detects GPU device groups at startup and adds the internal user to them. If you still see permission errors, check that you are passing the entire /dev/dri directory (not a single sub-device):

# Correct — pass the whole directory
--device /dev/dri:/dev/dri

# Wrong — single device may prevent group auto-detection for other nodes
--device /dev/dri/renderD128:/dev/dri/renderD128

To debug, find the device group on the host:

stat -c '%g' /dev/dri/renderD128
# Common groups: 'video' (44), 'render' (105) — varies by distro

NVIDIA GPU

Prerequisites (Linux hosts):

  1. Install NVIDIA drivers
  2. Install NVIDIA Container Toolkit

Note

On Windows, skip step 2. Docker Desktop provides the toolkit itself — you only need the NVIDIA Windows driver and the WSL2 backend. See Windows below. The docker run command itself is identical.

docker run -d \
  --gpus all \
  -e NVIDIA_VISIBLE_DEVICES=all \
  -e NVIDIA_DRIVER_CAPABILITIES=all \
  stevezzau/media_preview_generator:latest

Tip

Why NVIDIA_DRIVER_CAPABILITIES=all? Dolby Vision Profile 5 videos need the NVIDIA Vulkan driver to be tone-mapped; the all value is what makes that driver available inside the container. Without it, the app skips tone mapping for those files and their thumbnails come out with a green and purple tint. (The older compute,video,utility setting is fine for everything except Dolby Vision Profile 5.) If the dashboard still warns that those thumbnails will have the wrong colours, it says what is missing; see HDR and Dolby Vision.

Warning

Passing /dev/nvidia* as devices isn't enough. Without --gpus all or the NVIDIA runtime (runtime: nvidia in compose), the container gets the GPU but not the driver, so the GPU isn't detected and the log warns that the NVIDIA driver isn't in the container.

Tip

Multi-GPU? Hosts with two or more NVIDIA cards are detected automatically — each card appears as a separate row in Settings → Processing options → GPU device tuning with its own FFmpeg threads per worker setting. You choose which card each worker group uses in Settings → Workers.

Docker Compose:

services:
  plex-previews:
    image: stevezzau/media_preview_generator:latest
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

AMD GPU

docker run -d \
  --device /dev/dri:/dev/dri \
  -e PUID=1000 \
  -e PGID=1000 \
  stevezzau/media_preview_generator:latest

AMD requires proper VAAPI drivers on the host system. GPU device groups are auto-detected at container startup.

Windows

NVIDIA: works under Docker. Requirements:

  1. Docker Desktop using the WSL2 backend (Settings → General → Use the WSL 2 based engine) — GPU support is WSL2-only, it does not work with the Hyper-V backend.
  2. A current NVIDIA Windows driver with WSL2 GPU paravirtualization support. This is what provides CUDA and NVDEC inside WSL2.
  3. An up-to-date WSL2 kernel — run wsl --update.

Then use the same docker run command as Linux, including --gpus all; see NVIDIA GPU above. There is nothing to install inside WSL itself, and the NVIDIA Container Toolkit step is not needed — Docker Desktop wires that up for you.

Omit --device /dev/dri:/dev/dri on Windows. Under WSL2 that node is a WSLg/d3d12 stub rather than a real GPU, and passing it through can register a phantom device or muddy vendor detection.

Verify by opening the web UI and checking Settings → Processing options → GPU device tuning; your card should be listed with CUDA acceleration.

Note

Treat WSL2 as best-effort. GPU vendor detection via lspci is unreliable inside WSL2, so the app leans on nvidia-smi instead and logs a note saying so. If GPU jobs misbehave, run the container on a Linux host.

Plex installed on Windows (not in Docker): previews work as they are. Intro & Credits, chapter thumbnails, loudness and Library Health's full counts use Plex's database, which Docker Desktop's mount of a Windows folder can't open safely. Run the Plex database node for Windows beside Plex and add it as http://host.docker.internal:9494 (see Plex on another machine).

AMD and Intel: not accelerated under Docker. These rely on D3D11VA, a Windows-only framework that Docker Desktop's Linux VM cannot reach, so processing falls back to CPU. Add CPU workers in Settings → Workers to compensate. Or run a worker node on a Linux machine with the GPU.

macOS

Not accelerated under Docker, on either Apple Silicon or Intel. VideoToolbox is a macOS framework and Docker Desktop's Linux VM has no access to it, so processing uses CPU. Add CPU workers in Settings → Workers to compensate, or run the container on a Linux host for GPU acceleration.

Apple Silicon Macs still benefit from the native ARM64 image: it runs without Rosetta emulation, so CPU processing is faster than with an emulated x86 image.

Worker Configuration

A worker is a parallel slot that processes one file at a time. More workers means more files at once, but higher CPU/GPU load.

Workers are set up in worker groups under Settings → Workers. Each group has a name, when it is available (always, or on a weekly schedule) and up to 8 devices. Each device is the CPU or a specific GPU, with a worker count from 1 to 32 and the job types it runs (previews, intro & credits, and loudness on the CPU only). For CPU-only mode, use CPU devices only.

Settings → Processing options → GPU device tuning sets FFmpeg threads per worker for each GPU: how many CPU threads each GPU worker may use. The default is 2, and 0 means no cap.

Start with a few workers and raise the count while you watch system load. Devices on worker nodes appear here too. The Dashboard's Workers panel shows each group's and device's activity. Manage groups opens the group settings.


Unraid

Two install paths: the Community Applications template (easiest) or a manual docker run command (more control).

Easiest: search for media-preview-generator in Community Applications and install the template.

Quick start (either path):

  1. Run the container (CA template or docker run below).
  2. Open the Web UI at http://YOUR_UNRAID_IP:8080.
  3. Sign in with the token from auth.json in the app's config folder (/mnt/user/appdata/media-preview-generator in the example below), or set WEB_AUTH_TOKEN on the container.
  4. Complete the Setup Wizard — connect a server and review the options.

Tip

The setup wizard guides you through Plex sign-in, Emby login or API key, or Jellyfin Quick Connect — no need to copy tokens by hand for any of the supported vendors.

Manual Docker Run — Intel iGPU (Most Common)

docker run -d \
  --name media-preview-generator \
  --restart unless-stopped \
  -p 8080:8080 \
  -l net.unraid.docker.webui="http://[IP]:[PORT:8080]/" \
  --device /dev/dri:/dev/dri \
  -e WEB_AUTH_TOKEN=my-secret-password \
  -e PUID=99 \
  -e PGID=100 \
  -v /mnt/user/data/plex:/data/plex:ro \
  -v "/mnt/cache/appdata/plex/Library/Application Support/Plex Media Server":/plex:rw \
  -v /mnt/user/appdata/media-preview-generator:/config:rw \
  -v /etc/localtime:/etc/localtime:ro \
  stevezzau/media_preview_generator:latest
Setting Value Description
-l net.unraid.docker.webui Web UI label Adds a Web UI button in Unraid
--device /dev/dri GPU passthrough Intel VAAPI/QuickSync
WEB_AUTH_TOKEN my-secret-password Your login password (set your own!)
PUID/PGID 99/100 Unraid nobody:users account
/data/plex Media path Same as Plex container = no path mapping
/config App config Settings persist here

Intel iGPU with Custom Network (br0/br1)

If you use a custom Docker network with fixed IPs (common on Unraid):

docker run -d \
  --name media-preview-generator \
  --restart unless-stopped \
  --network=br0 \
  --ip=192.168.1.50 \
  --device /dev/dri:/dev/dri \
  -e WEB_AUTH_TOKEN=my-secret-password \
  -e PUID=99 \
  -e PGID=100 \
  -v /mnt/user/data/plex:/data/plex:ro \
  -v "/mnt/cache/appdata/plex/Library/Application Support/Plex Media Server":/plex:rw \
  -v /mnt/user/appdata/media-preview-generator:/config:rw \
  -v /etc/localtime:/etc/localtime:ro \
  stevezzau/media_preview_generator:latest

Note

When using --network with a fixed IP, you don't need -p 8080:8080 — access the web UI directly at http://192.168.1.50:8080

NVIDIA GPU on Unraid

Requires: Nvidia-Driver plugin from Community Applications.

docker run -d \
  --name media-preview-generator \
  --restart unless-stopped \
  --runtime=nvidia \
  -p 8080:8080 \
  -e NVIDIA_VISIBLE_DEVICES=all \
  -e NVIDIA_DRIVER_CAPABILITIES=all \
  -e WEB_AUTH_TOKEN=my-secret-password \
  -e PUID=99 \
  -e PGID=100 \
  -v /mnt/user/data/plex:/data/plex:ro \
  -v "/mnt/cache/appdata/plex/Library/Application Support/Plex Media Server":/plex:rw \
  -v /mnt/user/appdata/media-preview-generator:/config:rw \
  -v /etc/localtime:/etc/localtime:ro \
  stevezzau/media_preview_generator:latest

Warning

On Unraid, use --runtime=nvidia — not --gpus all. If you see docker: Error response from daemon: AMD CDI spec not found, that's Docker trying to resolve --gpus all through the Container Device Interface (CDI). Unraid doesn't ship CDI specs, so the daemon falls through to whatever spec it finds. Switching to --runtime=nvidia bypasses CDI and uses the Nvidia-Driver plugin directly.

Important Unraid Notes

PUID/PGID Values — Unraid uses nobody:users by default:

Variable Value Description
PUID 99 nobody user
PGID 100 users group

Emby or Jellyfin on Unraid — the commands above are for Plex. For Emby, or Jellyfin in its default layout, change /data/plex:ro to /data/plex:rw, because those previews are written next to each video. See Volume Mounts.

Network Considerations — When completing the Setup Wizard, make sure each media server you pick is reachable from the container (not localhost from Unraid's perspective). Plex servers appear in a dropdown after sign-in; Emby and Jellyfin are connected by URL.

Check Intel GPU Exists:

ls -la /dev/dri
# Should show: card0, renderD128

Path Mapping for Plex

Path mapping is only needed when this container mounts media to a different path than Plex uses.

No Mapping Needed (Recommended) — mount media to the same container path as Plex:

Plex:           /mnt/user/data/plex → /data/plex
This container: /mnt/user/data/plex → /data/plex  ← Same path!

With Path Mapping — if you prefer mounting to /media:

Plex:           /mnt/user/data/plex → /data/plex
This container: /mnt/user/data/plex → /media      ← Different path

Add the mapping in the wizard (step 3, Advanced) or later in Servers → Edit → Path mappings:

See Path Mappings for more examples.

TRaSH Guide Folder Structure

For users following TRaSH Guides:

  1. Configure Plex Container — add a second container path:
    • Container path: /server/media/plex/
    • Host path: /mnt/user/media/plex/
  2. Update Plex Libraries — use the new mapping:
    • Format: //server/media/plex/<media-folder>
    • Example: //server/media/plex/tv
  3. Set Permissions:
    # Make the Plex Media folder writable by the Unraid 'users' group (PGID=100)
    # so this app's container (running as PUID=99 / PGID=100) can write previews into it.
    chown -R 99:100 /mnt/cache/appdata/plex/Library/Application\ Support/Plex\ Media\ Server/Media/
    chmod -R 775   /mnt/cache/appdata/plex/Library/Application\ Support/Plex\ Media\ Server/Media/
    

    (Older guides recommended chmod 777 — that works but grants world-writable access; chown + 775 is the safer equivalent.)


Networking

Important

Use a routable IP address for your media servers, not localhost. The container can't reach localhost on your Docker host. If you set a server URL manually, use something like http://192.168.1.100:32400 (Plex), http://192.168.1.100:8096 (Emby/Jellyfin), etc. The Setup Wizard picks routable URLs for you when it can.

Quick Decision Tree

Where is the media server?
│
├── Same Docker host as this app?
│   ├── Server uses host network      → Use --network host
│   ├── Server uses bridge (default)  → Use same network or server IP:port
│   └── Server uses custom (br1, macvlan) → Use same custom network
│
└── Different machine?
    └── Use bridge network with the server's IP address

If Plex runs on another machine and you want Intro & Credits markers, chapter thumbnails or loudness, also run a Plex database node beside it.

The same logic applies whether the target is Plex (port 32400), Emby (port 8096), or Jellyfin (port 8096). If you run more than one server, check each is reachable from the container independently — the Servers page shows a red status dot on any server card the app can't reach.

Custom Network Example (Unraid)

docker run -d \
  --name media-preview-generator \
  --restart unless-stopped \
  --network=br1 \
  --ip=192.168.1.51 \
  -l net.unraid.docker.webui="http://[IP]:[PORT:8080]/" \
  --device /dev/dri:/dev/dri \
  -e WEB_AUTH_TOKEN=your-password \
  -e PUID=99 \
  -e PGID=100 \
  -v /mnt/user/data/plex:/data/plex:ro \
  -v "/mnt/cache/appdata/plex/Library/Application Support/Plex Media Server":/plex:rw \
  -v /mnt/user/appdata/media-preview-generator:/config:rw \
  -v /etc/localtime:/etc/localtime:ro \
  stevezzau/media_preview_generator:latest

Note

Use Unraid's PUID=99, PGID=100 (nobody:users).


Common Operations

View Logs

docker logs media-preview-generator          # All logs
docker logs -f media-preview-generator       # Follow logs

Update

docker pull stevezzau/media_preview_generator:latest
docker stop media-preview-generator
docker rm media-preview-generator
# Re-run your docker run command

Your /config/settings.json persists between upgrades, so server connections, worker groups, and schedules come back automatically after re-running the container.

Image Tags

Tag Source Use for
:latest Latest GitHub release Recommended. Stable.
:X.Y.Z (version) A specific release (e.g. :3.7.5) Pinning to a known-good version
:dev Every push to dev Bleeding edge — may break

See the releases page for version history and per-release notes.

The web UI's version banner behaves accordingly: :latest / pinned versions compare against the latest GitHub release; :dev compares the baked commit SHA against the dev branch HEAD on GitHub.


Next Steps


Edit this page on GitHub
Type to search the documentation.