Dynamic_Gravity's Guide to Install Jellyfin [VM or Baremetal]

Important Resources

Backstory

The Jellyfin project was started as a result of Emby’s decision to take their code closed-source, as well as various philosophical differences with the core developers. Jellyfin seeks to be the free software alternative to Emby and Plex to provide media management and streaming from a dedicated server to end-user devices.

Objectives

  • Install jellyfin
  • Enable jellyfin behind a reverse proxy
  • Enable hardware acceleration
  • Custom compile ffmpeg with CUDA (optional)

Scope

  • Jellyfin will be installed as a Systemd service on a virtual machine
  • Jellyfin will run on a minimal install of Rocky Linux (EL8)
  • Traefik is used for the proxy
  • An Nvidia Quadro will be used for hardware acceleration

Installation

System Dependencies

  1. Firstly, make sure you system is up to date. Reboot if necessary.
sudo dnf update --assumeyes
  1. Next, install some basic tools as well as the epel repo data.
sudo dnf install --assumeyes dnf-utils dnf-plugins-core vim tar lsof net-tools wget epel-release
  1. Then enable epel and powertools repositories.
sudo dnf config-manager --set-enabled epel
sudo dnf config-manager --set-enabled powertools
  1. Next, install the system dependencies of the jellyfin rpms. Don’t know why these are required but they are.
sudo dnf install --assumeyes at libicu

Now we can move on to the ffmpeg step.

FFMPEG Dependencies

  1. Enable the installation of ffmpeg via the addition of the rpmfusion free/non-free repositories. It is important that the ffmpeg package is visible to rpmdb because the jellyfin rpm’s list this as required. You can customize the the path to the ffmpeg binary later to use a custom one, which I will display later. Note: the ffmpeg version currently in the fusion repo is 4.2 which is sufficient for jellyfin <=10.7.7 but if you are trying to use >10.8.0-beta then you need ffmpeg >= 4.4 so you will need to get a higher version that what is currently available. Likely, you will have to roll your own.
sudo dnf install --assumeyes https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm
sudo dnf install --assumeyes https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-8.noarch.rpm
  1. Enable the rpm fusion repositories.
sudo dnf config-manager --set-enabled rpmfusion-free-updates
sudo dnf config-manager --set-enabled rpmfusion-nonfree-updates
  1. Install ffmpeg
sudo dnf install --assumeyes ffmpeg

Nvidia Dependencies

  1. Blacklist noveau driver in your /etc/default/grub and regen your grub config.
GRUB_CMDLINE_LINUX="rd.driver.blacklist=nouveau"
  1. Download required devel packages.
sudo dnf install --assumeyes kernel-devel-$(uname -r) kernel-headers-$(uname -r) 
  1. Download LTS Nvidia packages (AT this post in time the Long-Lived driver is 470.)
# add the CUDA repo it has everything we need from them
sudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo

# clear the cache
sudo dnf clean expire-cache

# install the latest dkms driver package
sudo dnf module install nvidia-driver:470-dkms
  1. Reboot your system and then check to see your drivers have been correctly initialized. If all goes well you should see something like the following when you run the SMI tool.

sudo nvidia-smi

Screen Shot 2022-03-30 at 5.15.57 PM

Installing CUDA (optional unless you plan to custom compile ffmpeg)

I am using a GP107GL [Quadro P600] so I need to target the 11.4 CUDA packages as identified by the CUDA VERSION: in the previous screenshot above.

  1. Note: this will download like ~1 GiB of packages so this one might take awhile.
sudo dnf install --assumeyes nvidia-gds-11-4
sudo dnf install --assumeyes cuda-11-4
  1. Add the following to your .bashrc to include cuda in your PATH
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
  1. Reboot again

  2. Confirm you see the cuda compiler
    nvcc --version

Custom Compile FFMPEG (optional, comes after CUDA)

:loudspeaker: This is going to be arduous. Make some coffee. :coffee:

Largely ripped off from here but with a few extra steps.

  1. Install dependencies.
sudo dnf install --assumeyes nasm yasm autoconf automake bzip2 bzip2-devel cmake freetype-devel gcc gcc-c++ git libtool make pkgconfig zlib-devel
  1. Create your project workspace.
sudo mkdir -p /opt/custom-ffmpeg/{src,bin,build}
sudo chown -R $USER:$USER /opt/custom-ffmpeg

BIN_DIR=/opt/custom-ffmpeg/bin
SRC_DIR=/opt/custom-ffmpeg/src
BUILD_DIR=/opt/custom-ffmpeg/build
  1. libx264
cd $SRC_DIR
git clone --branch stable --depth 1 https://code.videolan.org/videolan/x264.git
cd x264
PKG_CONFIG_PATH="$BUILD_DIR/lib/pkgconfig" ./configure --prefix="$BUILD_DIR" --bindir="$BIN_DIR" --enable-static
make
make install
  1. libx265
cd $SRC_DIR
git clone https://bitbucket.org/multicoreware/x265_git.git
cd x265_git/build/linux
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$BUILD_DIR" -DENABLE_SHARED:bool=off ../../source
make
make install
  1. libfdk_aac
cd $SRC_DIR
git clone --depth 1 https://github.com/mstorsjo/fdk-aac
cd fdk-aac
autoreconf -fiv
./configure --prefix="$BUILD_DIR" --disable-shared
make
make install
  1. libmp3lame
cd $SRC_DIR
curl -O -L https://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz
tar xzvf lame-3.100.tar.gz
cd lame-3.100
./configure --prefix="$BUILD_DIR" --bindir="$BIN_DIR" --disable-shared --enable-nasm
make
make install
  1. libopus
cd $SRC_DIR
curl -O -L https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
tar xzvf opus-1.3.1.tar.gz
cd opus-1.3.1
./configure --prefix="$BUILD_DIR" --disable-shared
make
make install
  1. libvpx
cd $SRC_DIR
git clone --depth 1 https://chromium.googlesource.com/webm/libvpx.git
cd libvpx
./configure --prefix="$BUILD_DIR" --disable-examples --disable-unit-tests --enable-vp9-highbitdepth --as=yasm
make
make install
  1. nv-codec-headers
cd $SRC_DIR
git clone https://github.com/FFmpeg/nv-codec-headers
cd nv-codec-headers
# this installs the headers system wide
sudo make install

# now copy them to the build directory
sudo cp -avr /usr/local/include/ffnvcodec $BUILD_DIR/include/
sudo cp -av /usr/local/lib/pkgconfig/ffnvcodec.pc $BUILD_DIR/lib/pkgconfig
sudo chown -R $USER:$USER $BUILD_DIR

This allows the ffmpeg configure script to auto detect all nvidia encode/decode flags

  1. Compile ffmpeg (you should all nvidia enabled options in the hwaccel area)
cd $SRC_DIR
curl -O -L https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2
tar xjvf ffmpeg-snapshot.tar.bz2
cd ffmpeg
PATH="$BIN_DIR:$PATH" PKG_CONFIG_PATH="$BUILD_DIR/lib/pkgconfig" ./configure \
  --prefix="$BUILD_DIR" \
  --pkg-config-flags="--static" \
  --extra-cflags="-I$BUILD_DIR/include" \
  --extra-ldflags="-L$BUILD_DIR/lib" \
  --extra-libs=-lpthread \
  --extra-libs=-lm \
  --bindir="$BIN_DIR" \
  --enable-gpl \
  --enable-libfdk_aac \
  --enable-libfreetype \
  --enable-libmp3lame \
  --enable-libopus \
  --enable-libvpx \
  --enable-libx264 \
  --enable-libx265 \
  --enable-nonfree

make
sudo make install

Congratz! :tada:

Now our custom ffmpeg is installed to /opt/custom-ffmpeg/bin/ffmpeg. This can literally convert any media you throw at it now.

Install and Setup Jellyfin

  1. Here is jellyfin’s official download page. It’s nice and pretty but for us RedHat folk we don’t get no frills. We go here → Jellyfin Stable CentOS Packages

We need to get the current latest stable, v10.7.7, download both the jellyfin-web and jellyfin-server.

cd /tmp
wget https://repo.jellyfin.org/releases/server/centos/stable/server/jellyfin-10.7.7-1.el7.x86_64.rpm
wget https://repo.jellyfin.org/releases/server/centos/stable/web/jellyfin-web-10.7.7-1.el7.noarch.rpm
  1. Now this is important, we must install the web rpm before the server rpm. If all goes well, it won’t complain about missing dependencies.
sudo rpm -Uvh /tmp/jellyfin-web-10.7.7-1.el7.noarch.rpm
sudo rpm -Uvh /tmp/jellyfin-10.7.7-1.el7.x86_64.rpm
  1. Setup the service and enable to run on boot
sudo systemctl enable --now jellyfin.service
  1. Open up firewall ports. Here is a description for what they are and their function:
  • 8096/tcp is used by default for HTTP traffic. You can change this in the dashboard.
  • 8920/tcp is used by default for HTTPS traffic. You can change this in the dashboard.
  • 1900/udp (ssdp) is used for service auto-discovery. This is not configurable.
  • 7359/udp is also used for auto-discovery. This is not configurable.
sudo firewall-cmd --zone=public --add-port=8096/tcp --permanent
sudo firewall-cmd --zone=public --add-port=8920/tcp --permanent
sudo firewall-cmd --zone=public --add-port=1900/udp --permanent
sudo firewall-cmd --zone=public --add-port=7359/udp --permanent
sudo firewall-cmd --reload
  1. SELinux (???)
  • I have it disabled on mine because I use a different mitigation strategy but if someone wants to help contribute please let me know and we’ll get it added.
  1. Run a quick test to see if you get the jellyfin page. You should see the contents of index.html page.
curl -L http://127.0.0.1:8096
  1. Proceed with the first time setup welcome splash page and you should be good to go :+1:

  2. Enable Hardware Acceleration (optional)

Login as the admin user and go to [server] -> [playback] -> [transcoding].

Check all of those sexy boxes and set your hardware accelerator to Nvidia NVENC.

Behind the Reverse Proxy (Traefik)

I use Traefik with the dynamic file provider configuration for my setup. I use Saltstack to manage the contents of the traefik.yml file but that is out of scope for this guide. Bellow is my following config for Jellyfin.

Pretty simple TBH. Just point what ever load balancer you use to what ever port you expect to use jellyfin on (:8096) and be sure to reference it correctly.

global:
  checkNewVersion: true
  sendAnonymousUsage: false
serversTransport:
  insecureSkipVerify: true
log:
  level: info
  filePath: /var/log/traefik/traefik.log
accessLog:
  filePath: /var/log/traefik/access.log
api:
  insecure: true
entryPoints:
  web:
    address: :80
  websecure:
    address: :443
certificatesResolvers:
  letsencrypt:
    acme:
      email: REDACTED
      storage: /etc/traefik/acme/acme.json
      keyType: EC384
      dnsChallenge:
        provider: cloudflare
        delayBeforeCheck: 0
providers:
  file:
    directory: /etc/traefik/dynamic/
traefik_dynamic:
  http:
    routers:
      jellyfin:
        rule: "Host(`jellyfin.example.com`)"
        service: jellyfin
        tls:
          certResolver: letsencrypt
    services:
      jellyfin:
        loadBalancer:
          servers:
            - url: http://<fqdn or ipv4>:8096
10 Likes

Note: if you stick with version 10.7.7 and the version of ffmpeg from fusion (4.2), and you install the bits about the official nvidia driver (sans cuda) then you will still get hardware acceleration out of the box, but you wont be able to tick all the boxes. This will, however, be a much faster install (<10 minutes).

I do not recommend the jellyfin-ffmpeg package because it never really seems to be able to play any of my media at least.

2 Likes

Looks like a great write up! Thank you. I thought I would add this here for those perhaps intimidated by or lacking the time to follow your write up. It should be helpful for anyone using Docker, but I’m going to write it based on my experience with Synology Docker specifically. First, a small FAQ to clear up possible confusion and explain the appeal of Jellyfin.

WHAT IS JELLYFIN? (There’s a gotcha here)
It’s actually a few things under the same name.

  1. Primarily it’s a service that provides access to your library of media (videos, music, books, etc) over the internet to client apps using credentialed users that are separate from the rest of your server.
  2. Jellyfin is also a variety of client applications by the same name, though you’re welcome to use 3rd party apps as well - or build your own.

WHY USE JELLYFIN…instead of Emby, Plex, Navidrome, etc.?

  • Jellyfin is and will remain open-source, unlike Emby and Plex.
  • Jellyfin Server is easy to setup and administrate.
  • Jellyfin Clients are easy to use with a minimalist UI that still has back-end intelligence to display useful, relevant information.
  • Jellyfin is extremely light on resource needs. Where Plex was choking my old NAS to death, Jellyfin runs smoothly.

On my DS412+ (a decade old NAS), here’s the comparison of service by sustained load observed CPU% and RAM consumption. Plex isn’t shown because it consumed so much it wouldn’t even run:

              |   Navidrome    |  Jellyfin
Supports      |     Music      |  Music, Movies, Shows, TV w/DVR, Books, Audiobooks, Comics, & IP radio.
Idle          |   1.0%/500MB   |  0.3%/200MB
Music         |   1.2%/600MB   |  0.3%/220MB 
Video (Direct)|      N/A       |  0.8%/1.0GB
Transcoding   |      N/A       |  70%/ 1.1GB

Editors note: I’ve not used Jellyfin for TV w/DVR, books, audiobooks, comics, or IP radio as I have no interest in those forms of media at this time.

For Synology users comfortable with Handbrake and Docker, or willing to learn, here’s my setup for all of my media needs. I obviated the need for hardware acc’l by converting my entire library using Handbrake as a pre-requisite to using Jellyfin.

Server:

  1. Install the Handbrake docker and follow it’s instructions to convert all of your video files to H.264 with AAC audio - set it to keep original resolution and a bit rate you’re comfortable with using. This takes a bit of learning, trial and error for your hardware configuration, and a lot of time to process once you’ve got your settings dialed in - but this will largely prevent streaming media from needing to transcode to compatible formats (typically MP4 with AAC), which will improve your viewing experience and reduce the hardware/network needs henceforth. Do it once, and do it right. While this will improve your viewing experience, it’ll also cut down on your power bill over time.
  2. Install the Jellyfin docker and follow it’s instructions to connect to your media libraries, add users, etc. This is very simple. Takes less than 5 minutes to setup with no prior knowledge.

Clients: (Click here for the recommended menu)

  • PC - I prefer in-browser for video and music, but a Win/Linux/Flatpak/Mac Jellyfin Client does exist. Both interfaces allow you to download media files for offline access, but require use of your native, 3rd party app for offline playback.
  • Android Mobile - Install “Jellyfin” from Play Store. For music, install Gelli.
  • Android TV - Install “Jellyfin for Android TV” from Play Store.
1 Like

Thank you for contributing :slight_smile:

Is there a guide for ffmpeg and Ubuntu server? I’m scratching my head. Jellyfin works fine for the most part but every once in a while jellyfin crashes and says restarted too many times. I think it has to do with opencl and not cuda based acceleration with my Quadro. I never had the issue with docker but my present vm doesn’t always like transcoding uhd video. Okay 1080p has no issues.

Yes, this one here in fact.

https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu

1 Like

Just an update, @Dynamic_Gravity 10.8 has been released and supports nvidia cuda acceleration instead of opencl.

Docker image would be

jellyfin/jellyfin:10.8.0

2 Likes

Thanks! I’ve been on beta3 for a bit but I’ll check out the official release when I get a moment.

1 Like

Any ideas why im having trouble with android tv and dolby vision not working. Supposedly it should work with 10.8 but doesnt. I have always used plex for my dolby vision stuff but my shield might be broken because plex when it does work buffers like mad with dolby vision. Whereas jellyfin on the same shield doesnt buffer with hdr10.