Today I’ll be deploying a Jellyfin server as a Docker container. I’ve used Jellyfin for several years now, and I find it to be an excellent media server. Prior to Jellyfin I use Plex. I have to admit, Plex is good, but Jellyfin is better. The set up I’m describing here is what I use for my own home server set up. It goes into a fair bit of detail on how to set up the shares on my system which probably won’t be exactly the same on your system. I’ve documented my server set up and I like it, there’s a fair bit of set up but once it’s working it’s robust and very feature complete.
Create the ZFS Datasets
This is the first place where you may find my setup isn’t relevant to you. I have a five disk RAIDZ2 array for storing our media hosted by my Proxmox server. This provides a good balance of features and means I only need to run a single server. Below I create a just one new dataset for movies but I have datasets for series, music, books and photos. Create datasets as you need them and then bind them into your servers.
Shell into your Proxmox server and create a movies dataset
zfs create tank/movies
Switch to the /tank
directory and own the movies folder with your user and the lxc-users
group – this is needed for the sharing.
# cd /tank # chown doozer:lxc-users movies/ # ls -la total 21 drwxr-xr-x 6 root root 6 Jul 17 14:17 . drwxr-xr-x 19 root root 4096 May 18 16:17 .. drwxr-xr-x 6 doozer lxc-users 6 Jul 11 17:24 home drwxr-xr-x 2 doozer lxc-users 2 Jul 17 14:17 movies drwxr-xr-x 30 doozer lxc-users 30 Jul 16 09:34 music drwxrwxr-x 2 doozer lxc-users 2 Jul 14 19:32 scratch
Map the ZFS Datasets
Now you need to map the new datasets into the container that is hosting the Samba service. Open the configuration file for the container.
nano /etc/pve/lxc/100.conf
Add mapping lines for the datasets you’ve created, the other settings should need changing (assuming you’ve already set up and tested sharing). As you can guess from the fact this is mp3
this isn’t my first mapping.
mp3: /tank/movies,mp=/mnt/movies
Restart the share container so that the mappings become active
Create the Shares
Log into Cockpit on the share container. Quickly go into Navigator and check the /mnt
folder contains the mappings you expect to see. Switch to File Sharing and create new shares for the mappings you’ve added. The only settings you need to fill in are share name (your choice), path (e.g. /mnt/movies) and to set Windows ACLs to on.
Mount the Shares on the Media Server
The media server is a virtual machine which hosts a number of Docker containers. Unfortunately you can’t bind mount into a virtual machine like you can into an LXC container so instead mount the Samba shares in the media server. Create a mount target file on the media server by logging in and then running the commands below. It’s fine if this directory is owned by root, the mount will override this.
cd /data/mnt sudo mkdir movies
Mount the share, the command to do this manually looks like the one below. This assumes you have a credentials file already created.
sudo mount -t cifs -o rw,vers=3.0,credentials=/root/.fileserver_smbcredentials,dir_mode=0775,file_mode=0775,uid=1000,gid=9999 //fileserver.example.co.uk/movies /data/mnt/movies
To mount the share permanently edit the /etc/fstab
file and add:
//fileserver.example.co.uk/movies /data/mnt/movies cifs rw,vers=3.0,credentials=/root/.fileserver_smbcredentials,dir_mode=0775,file_mode=0775,uid=1000,gid=9999
Restart the server to make sure the mounts happen cleanly.
Deploy Jellyfin on Docker Using Portainer and a Stack
When I first started using Portainer I went straight to using the GUI to configure containers but it turns out stacks are a much quicker and simpler way to get the same result. Rather than being presented with hundreds of options you aren’t going to use a stack just lets you specify what you need. The stack I’m using for Jellyfin is this
version: '3' services: jellyfin: image: jellyfin/jellyfin container_name: jellyfin user: 1000:1000 environment: - TZ=Europe/London volumes: - /data/jellyfin/config:/config - /data/jellyfin/cache:/cache - /data/mnt/movies:/data/movies:ro - /data/mnt/series:/data/series:ro - /data/mnt/photos:/data/photos:ro - /data/mnt/music:/data/music:ro - /data/mnt/books:/data/books:ro ports: - 8096:8096 - 8920:8920 - 1900:1900 - 7359:7359 restart: unless-stopped
Deploy Jellyfin on Docker Using Portainer
It is assumed you have already got the media service set up with Docker and Portainer. Jellyfin spoils us by shipping their own official container and providing good install instructions. Log into Portainer, select the environment to deploy to, select containers and click add container. Name the container jellyfin
and use the official image jellyfin/jellyfin
. Jellyfin requires a number of ports to be mapped. The install instructions a a bit quiet about which ports these are but they are here and shown below.
Under advanced options > command and logging make sure you set the UID and GID (probably to 1000:1000). For linuxserver containers you can (and probably should) also set the PUID and GUID environment variables. The Docker instructions provide a little more information. This Reddit post also discusses the issue,
Now you need to set up some bing mount volumes so shell into the media server and create the required directories. Jellyfin recommends two directories, one for config and the other for cache data. The jellyfin folder and it’s subfolders should all be owned by your user account.
cd /data sudo mkdir jellyfin sudo chown doozer:doozer jellyfin cd jellyfin mkdir config mkdir cache
Now fill in the volume bindings in Portainer. You’ll also need to make a number of bindings for the media which is in the directories we mounted earlier. For this install I’ve decided to try out the photo support in Jellyfin, I don’t trust it though so I’ve marked it read-only. I later added books and music, although I don’t currently use them, and I marked all the media bindings as read-only.
Network can be left on bridge. The only environment variable I set was TZ=Europe/London
which just sets the timezone. There are no labels to set. Restart policy should be set to unless stopped. There nothing to set under runtime and resources and capabilities can be left at the default settings. Click deploy the container.
Firewall
In Proxmox select the media server and open the firewall on port 8096 and 8920.
Setup and Testing
Visit the media server on the port specified above in the configuration e.g. http://media.example.co.uk:8096. You’ll be presented with a setup page. Follow the instructions to configure the system, for the most part that is just telling Jellyfin where it can find you media and what type the media is. One of the reasons I have so many bind mounts is because Jellyfin doesn’t handle mix libraries well. You can select subdirectories but I prefer this layout.