Ubuntu with NGINX

This guide covers how to start from nothing to running a secure Jellyfin server on your computer at home.

It is recommend you be familiar or at least comfortable with advanced computer use.

Installing Ubuntu

Simply put, Ubuntu is the go-to choice for Linux. Between Server and Desktop, it is nearly 50% of the market share.

This guide assumes you have an already working Windows computer. The second computer to be the dedicated server has nothing on it. Things you'll need:

  1. 4GB or larger USB drive.

  2. Ubuntu 20.04 ISO.

    • 20.04 is an LTS release. Closed source drivers from Nvidia, AMD and Intel support LTS releases only. 20.10 will not work.

  3. 128GB or larger SSD.

  4. Ethernet or supported WiFi card.

    • I highly recommend Ethernet. WiFi is not ideal. Additionally, Broadcom based WiFi cards require closed source drivers you must configure after installing Ubuntu or during with ethernet connected and "install third party" selected. If you must rely on WiFi, Intel based cards are natively supported in Ubuntu out of the box.

If all the criteria is met, you're good to go. Simply run Rufus and write Ubuntu to the USB in ISO mode. Boot from this USB on the computer you wish to install Ubuntu. When booting I personally prefer to use the Safe Graphics option if presented. Once the initial loading has completed, click "Try Ubuntu" to get a look at the desktop experience and poke around if you've never seen it before. Otherwise, click "Install Ubuntu". There are a few screens where you have options.

Updates and other software
Installation type
Updates and other software
  • Type of install [ Minimal | Normal ]

    • Since this is just functioning as a server, Minimal is all you need.

  • Download updates

    • This is optional. I don't select this because I just want the install to go as fast as possible. Everything can be updated later. I am going to recommend you select it if you're a novice Linux user.

  • Install third party

    • Yes you do. Just in case there is something that requires additional setup you otherwise wouldn't know what to do.

Installation type
  • Erase disk and install Ubuntu - This is the option you want to select. This is your dedicated server computer. If you already have a Windows install on this disk, past this point you likely won't have a need for it. This computer will run Ubuntu 24/7. If you have a storage disk in this computer, make sure you select the correct disk on the next screen!

Complete the rest of the prompts on screen and reboot when the installer tells you to.

Network Configuration

With Ubuntu installed, login to your router. This is either 192.168.1.1 or something like routerlogin.net. You need to make a few changes. If you're unable to to make these changes, such as using shared complex internet, you can't continue as this step is required.

  1. Find DHCP reservations. Assign your server computer a static IP for the local network. You'll do this with the MAC address. Instructions vary by router, so read the manual. It's the same basic method. You assign a MAC address like 00:AA:BB:CC:DD to an LAN IP like 192.168.1.50. To find the MAC in Ubuntu, open Settings, click Network, then click the cog icon to the right of current network connection. In this window it is known as "Hardware Address".

  2. Find Port Forwarding. Forward ports 80 and 443 to the static IP you just set. You do not need or want to forward Jellyfins default ports. Only the two mentioned here.

  3. Optional: While you're at it you can change your DNS to OpenDNS, Cloudflare DNS, or Google DNS so all devices benefit from it.

Installing Software

All of the software you install will be done through Terminal. It's not as complicated as it seems. To make Jellyfin accessible outside of your home network you configure a reverse proxy. There are a handful of choices available for you to pick from. They all do the same job to reach the same end result, how they get there varies. I personally use NGINX because I am used to it and it's more configurable (like Apache). That's what this guide will use. A common suggestion on reddit is to use Docker due to it's ease of use and simplicity.

CTRL + ALT + T brings up a Terminal window. Enter in sudo su and type your password. These commands are one liner. Just copy and paste the whole line in the code format.

> nginx

apt-get update && apt-get install nginx

NGINX will run as soon all dependencies are met and installed. You don't need to do anything further here.

> jellyfin

apt-get install apt-transport-https && wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | sudo apt-key add - && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | sudo tee /etc/apt/sources.list.d/jellyfin.list && apt-get update && apt-get install jellyfin

Jellyfin too will run once all dependencies are met and installed. You can access it at http://localhost:8096. Go through the initial setup process by adding a user. Do not create a user named "admin" as this unnecessary. Do not show users on the login screen. Do not allow unlimited login attempts (-1), this should be set to three (3). Don't worry about adding any media. You can do this later.

> cifs-utils

apt-get install cifs-utils

This is for sharing with NAS drives. You don't need to do anything further here.

> samba

apt-get install samba

This is for sharing with Windows. You don't need to do anything further here.

> certbot

This is optional. If you have no intentions of using your own TLD, skip installing.

apt-get install certbot

Certbot will create an SSL certificate for you using the service Lets Encrypt. You don't need to do anything further here.

> ufw

This is a firewall. It is a dependency in the previous installed packages. Check to see if you have it installed by running ufw status in Terminal. If you get a message stating not installed, install it.

apt-get install ufw

You don't need to do anything further here.

Media Location

Where is your media stored? Shared LAN drive? Or connected internal HDD? Both you and Jellyfin are going to need to be able to access this root location as you acquire new media. At least so you don't have to physically use this computer. I have a basic WD MyCloud (no apps version) NAS that houses all my media. It is a shared LAN drive. It needs to mount every time Ubuntu boots. Essentially permanent mount. If you're using an internal HDD, you're going need to share it's root folder over LAN. Follow the steps below for the method you're using.

LAN Drive (NAS)
Internal HDD
LAN Drive (NAS)
  1. gedit /etc/nsswitch.conf

  2. Add WINS to line 12 before the acronym DNS and save the file.

  3. cp /etc/fstab /etc/fstab_backup This backups your fstab file just in case.

  4. Assuming your network drive is password protected, complete steps 4 and 5. If it is not, skip these. You need to make a login file for the shared network drive so you don't have to worry about logging in every time you reboot. To do that, create a file using this command: touch /etc/shared_login && gedit /etc/shared_login

  5. Add your username and password to it on a separate line like this example and save it.

    • username=hello

    • password=world

  6. mkdir /media/shared Create a mount directory

  7. gedit /etc/fstab

  8. Add a mount point in your fstab file that links your shared location. You should know your network drive IP and share folder. I left mine in as an example. You will need to replace it with yours.

    • If you have a password protected drive: //192.168.1.100/Watch /media/shared cifs vers=3.0,credentials=/etc/shared_login,iocharset=utf8,file_mode=0777,dir_mode=0777,uid=box,gid=box,nofail 0 0

    • If you do not have a password protected drive: //192.168.1.100/Watch /media/shared cifs vers=3.0,iocharset=utf8,file_mode=0777,dir_mode=0777,uid=box,gid=box,nofail 0 0

  9. mount -a Run the mount process.

Internal HDD
  1. gedit /etc/nsswitch.conf

  2. Add WINS to line 12 before the acronym DNS and save the file.

  3. mkdir /media/Storage

  4. Open Disks from the launcher.

  5. Select the disk and the partition you want to use as the storage for media.

  6. Click the double cog icon and select Edit mount options.

  7. Toggle off User session defaults.

  8. Make sure Mount at system startup and show in user interface is ticked.

  9. Change the mount point to /media/Storage

  10. Click OK

  11. smbpasswd -a <yourUbuntuUserName>

  12. Enter a password

  13. gedit /etc/samba/smb.conf

# Paste this at the bottom
[JellyfinMedia]
path = /media/Storage
available = yes
valid users = <yourUbuntuUserName>
read only = no
browsable = yes
public = yes
writable = yes

Save the file and close it. You should restart the SAMBA service with systemctl restart smbd.service

On your Windows computer you can access it at \\192.168.1.xx\Storage

Windows Enterprise users: Visit Control Panel\User Accounts\Credential Manager to add this computer. Otherwise the default policy will block access to unauthenticated computers/shares.

NGINX Conf Files

Nginx uses .conf files to function. You're going to be editing an existing one and creating a new one.

gedit /etc/nginx/nginx.conf will bring up the base NGINX file in a text editor so you can make changes. Here's what mine looks like:

# v1.2
####
# Basic variables to run NGINX as a service.
####
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
####
# Manages NGINX connections.
####
worker_connections 2000;
multi_accept off;
}
http {
####
# Include basic configuration files.
####
include mime.types;
#include fastcgi.conf;
#include block.conf;
include jellyfin.conf;
####
# Remove server version from pages and header.
####
server_tokens off;
####
# Undefined mime types are downloaded instead of rendered.
####
default_type application/octet-stream;
####
# These are for load management.
####
sendfile on;
tcp_nopush on;
tcp_nodelay on;
proxy_buffering off;
####
# These cache frequently accessed files.
####
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
####
# Compress contents.
# Files that match the mime types that are larger than 100kb are compressed.
# You may change gzip_comp_level.
# 1 is low compression using low CPU.
# 9 is high compression using high CPU.
####
gzip on;
gzip_vary on;
gzip_min_length 100;
gzip_comp_level 4;
gzip_buffers 16 8k;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/json application/x-javascript application/javascript application/xml application/xml+rss application/xhtml+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject font/woff font/woff2 font/otf font/ttf image/gif image/jpeg image/png image/x-ms-bmp image/svg+xml image/webp image/tiff;
gzip_disable "MSIE [1-6]\.";
####
# Set expirary headers to keep contents in cache.
# This reduces server load and speeds up browsing.
# You need to connect to this per site.
####
map $sent_http_content_type $expires {
default off;
text/html epoch;
text/css max;
application/javascript max;
font/woff max;
font/woff2 max;
font/otf max;
font/ttf max;
~image/ max;
}
}

To create and edit the config file for Jellyfin, do the following

touch /etc/nginx/jellyfin.conf && gedit /etc/nginx/jellyfin.conf For reference, here is what mine looks like:

# v1.2
server {
####
# Jellyfin HTTP Redirect.
####
listen 80;
listen [::]:80;
server_name jellyfin.example.com;
return 301 https://$host$request_uri;
}
server {
####
# Jellyfin HTTPS main.
# This is the HTTPS server block
####
listen 443 ssl http2;
listen [::]:443 ssl http2;
#server_name jellyfin.example.com;
#resolver example.duckdns.org valid=30s;
set $jellyfin 127.0.0.1;
####
# SSL Settings.
# chain.pem only contains the intermediate certificate.
# fullchain.pem contains both server certificate and the intermediate certificate.
# cert.pem is used to verify client certificates and OCSP responses if ssl_stapling is enabled.
# You should always use fullchain.pem when configuring the server certificate.
# dhparam.pem is used to increase security https://security.stackexchange.com/a/94397
####
ssl_certificate "/etc/letsencrypt/live/jellyfin.example.com/fullchain.pem";
ssl_certificate_key "/etc/letsencrypt/live/jellyfin.example.com/privkey.pem";
ssl_client_certificate "/etc/letsencrypt/live/jellyfin.example.com/cert.pem";
ssl_trusted_certificate "/etc/letsencrypt/live/jellyfin.example.com/chain.pem";
ssl_dhparam "/etc/letsencrypt/live/dhparam.pem";
add_header Strict-Transport-Security "max-age=31536000" always;
ssl_stapling on;
ssl_stapling_verify on;
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
####
# Security / XSS Mitigation Headers for old browsers. Newer browsers may ignore
####
add_header X-Content-Type-Options "nosniff";
####
# Connect to expiary headers to cache contents
####
expires $expires;
####
# Access logging.
####
access_log /var/log/nginx/jellyfin.log;
####
# Content Security Policy for modern browsers
# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
# Enforces https content and restricts JS/CSS to origin
# External Javascript (such as cast_sender.js for Chromecast) must be whitelisted.
####
add_header Content-Security-Policy "default-src https: data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";
location = / {
return 302 https://$host/web/;
}
location / {
####
# Proxy main Jellyfin traffic
####
proxy_pass http://$jellyfin:8096;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
}
location = /web/ {
####
# This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/
# Similar to a rewrite rule
####
proxy_pass http://$jellyfin:8096/web/index.html;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
}
location /socket {
####
# Poxy Jellyfin Websockets traffic
####
proxy_pass http://$jellyfin:8096;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
}
}

DNS

Let's talk about that. Specifically, these lines:

#resolver "example.ddns.com";
#set $jellyfin 127.0.0.1;

Hopefully you have already acquired and setup a DDNS service. And/or optionally a TLD.

Manual DNS
Automatic DNS Purchased Domain
Automatic DNS Free Domain
Manual DNS

This is a DNS record managed by you. If you purchased your own domain from a provider like GoDaddy, you can use this option. If your dynamic home address changes, you need to update your record manually by logging into the domain sellers website. If you happen to have a static IP purchased from your ISP (in most cases business packages) this is the ideal method. Most people would recommend you don't do it this way. Home IPs are known to be ever revolving in certain areas. If your ISP is IPv6 only or prefers it over IPv4, you're in luck as these are highly unlikely to change.

With a purchased domain you can point a subdomain to your home IP address. To get your home IP you can search "what is my IP?" and copy it. Create a new A (IPv4) or AAAA (IPv6) record for your domain. It would look like one of these:

Type

Name

Value

TTL

A

jlyfn

50.100.50.100

1 hour

AAAA

jlyfn

FEDC:BA98:7654:3210:FEDC:BA98:7654:3210

1 hour

Do not use A and AAAA records at the same time. Use one or the other.

The "name" will be the subdomain where you are going to access your server (jlyfn.example.com) and the "value" is your home IP. Using a subdomain allows certain features of Jellyfin to work as expected. Using Jellyfin at a base URL in "sub directory" format can break features. You can read more at the link below.

In your conf file uncomment this line:

set $jellyfin 127.0.0.1;

You don't need the resolver line since you're pointing your domain name to your IP. Leave it commented out. Then change server_name to your new domain:

server_name jlyfn.example.com;

I use this method. My home IP hasn't changed since I've been here. And it's close to five years. Other people might not have this same experience. I use IPv6.

Automatic DNS Purchased Domain

This is a DNS managed for you. If your dynamic home address changes often, a DDNS service has you covered. With a purchased a domain you can still leverage a DDNS service. This is a recommend method for home users.

With your DDNS service and purchased domain you can point a subdomain to your DDNS URL. Create a new CNAME record for your domain. It would look something like this:

Type

Name

Value

TTL

CNAME

jlyfn

example.duckdns.org

1 hour

The "name" will be the subdomain where you are going to access your server (jlyfn.example.com) and the "value" is your DDNS URL. Using a subdomain allows certain features of Jellyfin to work as expected. Using Jellyfin at a base URL in "sub directory" format can break features. You can read more at the link below.

A running background task will keep your IP updated without user interaction. You should donate to the service if it helps you!

In your conf file uncomment these lines and edit the resolver line to your DDNS URL:

resolver "example.duckdns.org";
set $jellyfin 127.0.0.1;

Then change server_name to your new domain:

server_name jlyfn.example.com;

Your DDNS URL can also be used to access your server. This means jlyfn.example.com will lead to your Jellyfin service, and example.duckdns.org can show a "welcome to nginx" page. Of course your browser will also show a HTTPS error saying the certificate doesn't match. If you do not want the DDNS URL to show a page, you can block the request.

Create a new conf file: touch /etc/nginx/block.conf && gedit /etc/nginx/block.conf and paste in the following contents:

server {
listen 80;
listen [::]:80;
server_name _;
return 444;
}

Then edit your nginx.conf file: gedit /etc/nginx/nginx.conf and add block.conf to your includes:

http {
####
# Include basic configuration files
####
include mime.types;
include block.conf; <---- add this bit here
#include fastcgi.conf;
include jellyfin.conf;
[the rest of your conf file is below...]

A DNS cache flush may be required to process this if you visited your DDNS URL already: systemd-resolve --flush-caches

Automatic DNS Free Domain

If you intend to run multiple services or websites you cannot use this method. You cannot assign the same URL to the server_name for different services or websites.

This is a DNS managed for you. If your dynamic home address changes often, a DDNS service has you covered. You don't need to purchase a domain if you choose this method. This is a recommend method for home users.

With your DDNS service you can use this a direct method to access your server. For example if you signed up with Duck DNS, you would go to example.duckdns.org to access your media server.

Using a subdomain allows certain features of Jellyfin to work as expected. Using Jellyfin at a base URL in "sub directory" format can break features. You can read more at the link below.

A running background task will keep your IP updated without user interaction. You should donate to the service if it helps you!

In your conf file uncomment this line:

set $jellyfin 127.0.0.1;

You don't need the resolver line since you're pointing your DDNS domain to your IP. Leave it commented out. Then change server_name to your DDNS domain:

server_name example.ddns.com;

Assigning sever_name to both 80 and 443 blocks prevents a warning message. If you decided to run another website, nginx won't throw an error saying it can't bind 80 or 443 because it's already in use and ignore it. So make sure you enter your domain name on both.

Documentation also includes additional configurations for your conf files.

SSL

If you are using your own purchased TLD, please use SSL. If you are using a DDNS domain, you do not have to complete this section.

While this is technically optional, I highly discourage using unencrypted remote access! Passwords and any API keys that may be in use are exposed. It's like walking into a store naked.

You need to get a cert. You have two ways:

Automatic
Manual
Automatic

You do not need to do step one (1) titled "SSH into the server" because you are physically in front of the computer.

If using this route, you'll can leave the SSL lines commented out. Certbot can handle this for you.

Manual
  1. certbot certonly

  2. Enter in your email

  3. Choose option 1 to spin up a temporary webserver.

  4. Enter your domain name (jlyfn.example.com)

  5. Your cert will be generated for you. Edit the paths in your jellyfin.conf to point to your SSL files and uncomment them.

    • They are at /etc/letsencrypt/live/jlyfn.example.com. You realistically only need to change the URL portion.

Once you have obtained your cert you also need to generate a Diffie-Hellman key (dhparam.pem). This buffs up protection against attackers. Making it exponentially harder for an attacker to decipher server to client communication. Complete the following:

  1. Change path

    • cd /etc/letsencrypt/live

  2. Generate your pem file.

    • openssl dhparam -dsaparam -out dhparam.pem 4096

  3. Uncomment the ssl_dhparam line.

Firewall

Jellyfin remote connections are being handled through reverse proxy. There is no need to have the WAN IP allow remote connections. Jellyfin can disable this. To do that head to http://localhost:8096/web/index.html#!/networking.html and uncheck "Allow remote connections to this Jellyfin Server." Restart the server from the dashboard and verify from an externally connected device. Easiest way to do that is turn off your phones WiFi and visit your home IP in a web browser (Example: http://50.100.50.100:8096.) If you see a Forbidden message the port is allowing connections, but Jellyfin is stopping it. Allowing remote connections through the WAN IP is insecure and bypasses SSL. Because IPv6 violates HTTP URL formatting rules, you can't access an IPv6 IP directly from your address bar.

You'll want to setup your firewall (ufw) keeping all unnecessary ports closed. You want 80, 443 and all local (LAN) devices open. By default ufw is disabled. Set the default rules and enable it.

  1. ufw default deny incoming

  2. ufw default allow outgoing

  3. ufw enable

Once ufw starts, everything coming in is blocked. You can fix that by looking at the table.

Command

Purpose

Need

ufw allow 80

HTTP

Required

ufw allow 443

HTTPS

Required

ufw allow proto tcp from 192.168.1.0/24 to 192.168.1.xx

LAN

Required

ufw allow 21

FTP

Optional

ufw allow 22

SSH

Optional

For LAN connections, please modify 192.168.1.xx to the current computers IP address. You should already know this from earlier. A wide scope for LAN as shown above should allow SAMBA/SMB to work without issue for all devices on the network. A timeout error or connection refused means the port is closed. If you need to open additional ports for other services, please have a look at the list linked below or use network monitoring tools.

As a last resort, you use can NGINX to close the connection (444). You can add this to your jellyfin.conf file under the server block for 443 (SSL):

# This closes the connection if someone uses your WAN IP
if ($host != "sub.example.com") {
return 444;
}

This piece isn't necessary by any means if you see Jellyfin or ufw is working as intended. If the connection is allowed at the WAN IP, you can use this. if statements are a last resort here because Jellyfin and the firewall are capable of stopping requests.

Hardware Acceleration

As mentioned in the opening, Linux hardware acceleration can be tricky. YMMV when it comes to dedicated GPUs. From what I could find on the internet, Nvidia and AMD drivers are hit or miss for people. Where as Windows it's just a click to install and it all works. Before completing any section, please run apt update && apt upgrade

Check your Mesa drivers. 20.1 or later is required for proper hardware acceleration. Here's the command you run and its results:

> dpkg -l *mesa*
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-=========================-=======================-============-====================================================>
ii libegl-mesa0:amd64 20.2.6-0ubuntu0.20.04.1 amd64 free implementation of the EGL API
ii libgl1-mesa-dri:amd64 20.2.6-0ubuntu0.20.04.1 amd64 free implementation of the OpenGL API
ii libglapi-mesa:amd64 20.2.6-0ubuntu0.20.04.1 amd64 free implementation of the GL API
ii libglx-mesa0:amd64 20.2.6-0ubuntu0.20.04.1 amd64 free implementation of the OpenGL API
ii mesa-va-drivers:amd64 20.2.6-0ubuntu0.20.04.1 amd64 Mesa VA-API video acceleration drivers
ii mesa-vdpau-drivers:amd64 20.2.6-0ubuntu0.20.04.1 amd64 Mesa VDPAU video acceleration drivers
ii mesa-vulkan-drivers:amd64 20.2.6-0ubuntu0.20.04.1 amd64 Mesa Vulkan graphics drivers

This denotes Ubuntu managed packages are version 20.2.6. You can manually add a more up-to-date stable version from a PPA if you desire. These may include better performance and bug fixes. In rare cases it may be worse: add-apt-repository ppa:kisak/kisak-mesa - You need to reboot after updating.

Removal of this PAA is easy: apt install ppa-purge && ppa-purge -d focal ppa:kisak/kisak-mesa

AMD GPU PRO
Nvidia NVDEC/NVENC
Intel QSV
AMD GPU PRO

AMD GPU PRO driver on Linux requires a kernel downgrade because 20.04 ships with a newer one. This is fine and poses no problem as changing kernel versions in Linux is a breeze. The open source AMD GPU driver cannot transcode. It requires the non-free closed source version known as "PRO" in addition to the AMF package.

Installing the driver requires the following steps with Terminal:

  1. uname -r will show your current kernel version. 20.04 ships with 5.8.0-43-generic. You might see 5.8.0-44-generic or newer after running updates. This is fine.

  2. Install 5.4 LTS kernel as required by the AMD PRO driver. Kernel versions 5.8 will fail to compile and you will be left with a black screen on reboot.apt install linux-image-5.4.0-58-generic linux-headers-5.4.0-58-generic linux-modules-extra-5.4.0-58-generic

  3. Backup grub cp /etc/default/grub /etc/default/grub_backup

  4. Edit grub nano /etc/default/grub

  5. Make the default line look like the following GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.4.0-58-generic"

  6. Save with CTRL + O and CTRL + X

  7. Reboot.

  8. uname -r should now show 5.4.0-58-generic

  9. Download the Linux driver for Ubuntu and extract contents https://www.amd.com/en/support/kb/release-notes/rn-amdgpu-unified-linux-20-45

  10. Because of changes to the installer, many flags and scripts are depreciated and/or removed. Therefore the y are no longer needed. To install the driver from the root of the extracted folder, use the following command ./amdgpu-install --pro --opencl=legacy,rocr

  11. Please be patient as the driver takes several minutes to install. When it finishes you need to reboot. If you received any errors during the install, DO NOT reboot. This a compilation error and will result in a black screen. You should instead uninstall to try again. ./amdgpu-install --uninstall

  12. Once rebooted, install AMF apt install amf-amdgpu-pro

  13. You can stop here and leave your current install as is, or you can complete the optional steps remaining.

  14. Remove the 5.8 kernel as it's no longer needed. apt remove linux-image-5.8.0-43-generic linux-image-unsigned-5.8.0-43-generic apt remove linux-image-5.8.0-44-generic linux-image-unsigned-5.8.0-44-generic apt purge *5.8.0-43* apt purge *5.8.0-44* apt autoremove

  15. Edit grub nano /etc/default/grub

  16. Make the default line look like the following GRUB_DEFAULT=0

  17. Save with CTRL + O and CTRL + X

  18. Reboot

Theoretically, you should be done. However, it is at this point I still could not get the RX580 to transcode. I could only get VAAPI to work on the RX580. AMF failed. I have no idea why. Everything I typed in and looked over matched exactly what the documentation and examples showed/described. Because I could not under any circumstance get it to work (hours of searching), I simply gave up. An HEVC transcode was 45 fps. An AVI transcode was 170 fps. On Windows where I used to run Jellyfin, I could see transcodes as high as 1000 fps. I didn't understand it. I removed the RX580 and enabled the IGPU in BIOS. I followed those instructions to enable QSV and it worked.

Nvidia NVDEC/NVENC

Nvidia purposely impose a limit on their consumer grade (gaming) GPUs. NVENC/NVDEC by limitation of the driver is capped at a set amount. This varies based on GPU model, but the limit is often three (3). If you want to see what your GPU is capable of, look over the matrix for "Max concurrent sessions". They do this so you will purchase a business grade (commercial/working) GPU known as Quadro. Luckily the Linux community is ahead of this. They provide altercation scripts that remove this so you can use your GPU beyond this artificial limit. AMD and Intel do not impose limits on their GPUs.

You can download the Nvidia driver package for Linux at https://www.nvidia.com/Download/Find.aspx

Or you can use Terminal and enter apt install nvidia-driver-xxx - where xxx refers to the driver version you want to install. An example would be nvidia-driver-460

Intel QSV

Out of the box Ubuntu will have already configured VAAPI for you. You can verify this by using the follow commands

  • vainfo

  • vdpauinfo

  • journalctl -b | grep -iE 'vdpau | dri driver'

This matters because QSV non-free is based off VAAPI. If this is configured, you can apply the non-free variant. If it is not, configure it. Rather than using Ubuntu's version which stays behind updates, you should use the Intel official release. Copy the following text into an empty file and save it as a script .sh such as intel-non-free.sh

#!/bin/bash
echo ** Updating intel-media-va-driver-non-free driver
DEBIAN_FRONTEND=noninteractive
apt update
apt install -y gpg-agent wget
wget -qO - https://repositories.intel.com/graphics/intel-graphics.key | apt-key add -
echo 'deb [arch=amd64] https://repositories.intel.com/graphics/ubuntu focal main' >> /etc/apt/sources.list
apt update
apt install --only-upgrade -y intel-media-va-driver-non-free

From the location you saved it to, run the script ./intel-non-free.sh and then check the status with vainfo | grep iHD

Binge

You're done. You can add media and start watching. nginx -s reload will reload your conf files and your server should be accessible from your custom domain. If you encounter any errors you can ask for help on reddit. Don't go around the web publicly posting your server URL. Be selective with who you share it with.

Contributions

Something wrong? Want to add info? Help out! Let's discuss it.