How to: Install Jellyfin on a Synology NAS using Docker (with Intel QSV hardware transcoding)

Updated 22/02/23 to include required /dev/dri permissions for non-root users

There is an overwhelming amount of information regarding this topic, with the vast majority being outdated. As such, I am writing this post to provide an up to date method of installing Jellyfin on a Synology NAS (in my case, a DS920+) with the required configuration to support hardware transcoding. This assumes you have a compatible NAS, since CPUs used in Synology’s latest offerings do not have an iGPU and as a result, do not support hardware transcoding.


  • Basic Docker and Docker Compose knowledge.
  • A compatible Synology NAS.
  • Docker installed (the Package Center version is fine to use).
  • Docker Compose installed (if you’re not using the Package Centre version of Docker).
  • Set /dev/dri/ permissions (if running as non-root) as documented below.

Setting /dev/dri permissions (if running as non-root user)

Unlike other OSs, Synology DSM does not permit non-root users from accessing /dev/dri which will prevent hardware transcoding from working within your Jellyfin instance if you plan on running this as a non-root user:

[AVHWDeviceContext @ 0x561260cc5a80] No VA display found for any default device.
Device creation failed: -22.
Failed to set value 'vaapi=va:,driver=iHD,kernel_driver=i915' for option 'init_hw_device': Invalid argument
Error parsing global options: Invalid argument

To resolve this issue, you simply need to run the command “chmod 777 /dev/dri/*”, however this permission is not persistant so rebooting your device will result in hardware transcoding breaking again. To get around this, create a triggered task within your DSM Control Panel with the following options:

  • Task: Jellyfin driver permissions (this part really doesn’t matter)
  • User: root
  • Event: Boot-up
  • Enabled: Checked
  • User-defined script: chmod 777 /dev/dri/*

Click “OK” to create the task, then manually run the task once.

Creating the container

In order to create the container, we are going to use a Docker Compose YAML file which will define its properties and other configurations. On Synology DSM, you have a few options for using Docker Compose, none of which are available from the Docker app’s UI.

  • Portainer – My preferred method, since it provides you with a GUI and only requires SSH for the initial install (guide here).
  • SSH (here’s a good guide on this method).
  • Task scheduler – I’d avoid this method as it makes management messy.

You can find my Compose YAML for Jellyfin below:

version: "3.8"
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    user: xxx:xxx #your UID:GID
      - TZ=Europe/London #your time zone
      - JELLYFIN_PublishedServerUrl=x.x.x.x #your NAS IP
      - /volume1/docker/jellyfin:/config
      - /volume1/docker/jellyfin/cache:/cache
      - /volume1/video:/data/media
      - /dev/dri:/dev/dri
      - 8096:8096 #required web port
      - 8920:8920 #optional https port
      - 7359:7359/udp #optional discovery port
    restart: unless-stopped

A few notes about my configuration compared to others I’ve seen:

  • I’m using the official Jellyfin image rather than, since this now supports Intel QSV
  • As I’m not using the image, I do not need to include the Opencl-Intel Docker Mod.
  • I’m mapping /dev/dri (for HW transcoding support).
  • I’m using the same media volume as Video Station.
  • I’ve specified a cache directory to ensure it’s actually where you want it, rather than in the default, hidden location.
  • An additional port (1900/udp) used for DLNA is not included as this conflicts with default Synology ports. You can work around this by using a MacVLAN network, however, as this port is optional, I have skipped it in this guide.

Key changes you must make before deploying your container:

  • Set the UID and GID values. If you’re using what these should be set to, I’d recommend creating a new “docker” standard user and checking its values using SSH command “id username“. There is no requirement to run this container as root (either by removing the PUID/PGID or setting them to 0/0) and doing so decreases security unnecessarily.
  • Set your time zone using the TZ database name
  • Set your NAS IP (PublishedServerUrl)
  • Review the configured volumes and tweak as required
  • Manually create a “jellyfin” folder and “cache” subfolder within your Docker shared folder. This won’t happen automatically and will cause deployment to fail if it’s missing.

Enabling hardware transcoding

Once Jellyfin has been deployed and configured, you must manually enable hardware transcoding. Start by opening the admin dashboard, then select Playback:

Once here, change the Hardware acceleration drop down from None to Intel QuickSync (QSV).

Select your file types (AV1 is not supported so leave it unchecked), as well as the following:

  • Enable Enable hardware transcoding.
  • Ensure both low-power options are disabled (enabling either of these options breaks transcoding).
  • Enable both tone mapping options.

After scrolling to the bottom of the page and clicking Save, you should now have a working Jellyfin container with hardware transcoding support. If you’re facing issues, please leave a comment and I will do my best to help.

10 thoughts on “How to: Install Jellyfin on a Synology NAS using Docker (with Intel QSV hardware transcoding)”

  1. Thank you for posting! I’m doing right now on a DS720+ since the hardware is the same. Did you noticed good performances ?

  2. Hi, Thanks for your guide. I tried it with this on my 720+ but still running in the following error. I also add the puid and pgid to the local dsm user.
    Do you have any idea to solve this?

    ffmpeg version 5.1.3-Jellyfin Copyright (c) 2000-2022 the FFmpeg developers
    built with gcc 10 (Debian 10.2.1-6)
    configuration: –prefix=/usr/lib/jellyfin-ffmpeg –target-os=linux –extra-libs=-lfftw3f –extra-version=Jellyfin –disable-doc –disable-ffplay –disable-ptx-compression –disable-static –disable-libxcb –disable-sdl2 –disable-xlib –enable-lto –enable-gpl –enable-version3 –enable-shared –enable-gmp –enable-gnutls –enable-chromaprint –enable-libdrm –enable-libass –enable-libfreetype –enable-libfribidi –enable-libfontconfig –enable-libbluray –enable-libmp3lame –enable-libopus –enable-libtheora –enable-libvorbis –enable-libopenmpt –enable-libdav1d –enable-libwebp –enable-libvpx –enable-libx264 –enable-libx265 –enable-libzvbi –enable-libzimg –enable-libfdk-aac –arch=amd64 –enable-libsvtav1 –enable-libshaderc –enable-libplacebo –enable-vulkan –enable-opencl –enable-vaapi –enable-amf –enable-libmfx –enable-ffnvcodec –enable-cuda –enable-cuda-llvm –enable-cuvid –enable-nvdec –enable-nvenc
    libavutil 57. 28.100 / 57. 28.100
    libavcodec 59. 37.100 / 59. 37.100
    libavformat 59. 27.100 / 59. 27.100
    libavdevice 59. 7.100 / 59. 7.100
    libavfilter 8. 44.100 / 8. 44.100
    libswscale 6. 7.100 / 6. 7.100
    libswresample 4. 7.100 / 4. 7.100
    libpostproc 56. 6.100 / 56. 6.100
    [AVHWDeviceContext @ 0x56256f7c47c0] No VA display found for any default device.
    Device creation failed: -22.
    Failed to set value ‘vaapi=va:,driver=iHD,kernel_driver=i915’ for option ‘init_hw_device’: Invalid argument
    Error parsing global options: Invalid argument

      • Thanks for the fast response und support 🙂
        Yes, I get the same error when I remove the user variable. I tried it also with the root user. Same ids in the docker and on synology dsm.

        • That’s very strange! It sounds like the permissions are wonky. Is it a recent DSM installation?

          If you’re able to SSH onto the NAS, run “ls -la /dev/dri” and post the output here so we can see the device permissions.

  3. Yes it is a normal DSM installation. I checked the permission on dev/dri
    drwxr-xr-x 2 root root 80 May 15 22:05 .
    drwxr-xr-x 16 root root 14400 May 18 11:37 ..
    crwxrwxrwx 1 root root 226, 0 May 15 22:05 card0
    crwxrwxrwx 1 root videodriver 226, 128 May 15 22:05 renderD128

    root user id on DSM
    id root
    uid=0(root) gid=0(root) groups=0(root),2(daemon),19(log)

    root user id from jellyfin docker
    id root
    uid=0(root) gid=0(root) groups=0(root),2(daemon),19(log)


Leave a comment