Running your own Jellyfin server is a great way to keep all your movies, TV shows, music and photos in one place and stream them whenever you want. Instead of relying on external streaming services, you stay in full control of your files, user accounts and who can access your content across your devices.

Free VPS Trial
Try a virtual server risk-free for 30 days

Try out your VPS for 30 days. If you're not satisfied, you get your money back.

What is Jellyfin?

Jellyfin is an open-source media server that lets you organize and manage your own media col­lec­tion and stream it to different devices through a browser or app. It supports common media types like movies, TV shows, music, and photos, and includes features like user accounts, metadata, cover art, playback tracking and streaming over your network.

Because Jellyfin server is self-hosted, it runs in­de­pen­dent­ly of external providers. You only need an internet con­nec­tion for things like down­load­ing metadata or enabling remote access. Inside your home network, Jellyfin uses HTTP on port 8096 by default. You can also enable HTTPS if needed. For external access, the official doc­u­men­ta­tion rec­om­mends using a reverse proxy with trusted cer­tifi­cates.

Which server re­quire­ments do you need for Jellyfin?

Before in­stalling Jellyfin, it’s worth taking a moment to choose the right server setup. It’s not just about how many files you want to store. What really matters is whether your devices can play those files directly or if the server needs to convert them during playback. That con­ver­sion process is called transcod­ing, and it can use a lot of resources. Things like 4K content, HDR-to-SDR tone mapping, or multiple streams at the same time can quickly put pressure on your CPU or GPU.

For typical setups, Jellyfin rec­om­mends at least 8 GB of RAM. If you’re running a lean Linux server without a graphical interface, 4 GB may be enough. For hardware ac­cel­er­a­tion on non-Apple systems, Intel graphics are usually the easiest option to work with and offer solid encoding per­for­mance.

Let’s look at a few different scenarios so you can see which setup makes the most sense for your needs.

In­di­vid­u­als and families

If you’re mostly using Jellyfin on your own or with 2 or 3 other people and your files are already in common video formats like MP4 with H.264, a VPS is often all you need. In this setup, the server mainly delivers files instead of con­vert­ing them every time you press play. A solid starting point is a Linux VPS with at least 4 vCores, 4 GB RAM and around 120 GB of NVMe storage. If you want a bit more breathing room for larger libraries, metadata, or oc­ca­sion­al transcod­ing, 6 vCores, 8 GB RAM, and 240 GB NVMe storage will feel more com­fort­able.

The biggest advantage here is the cost-to-per­for­mance ratio. You get enough power for a personal media server without needing to invest in dedicated hardware right away. This works es­pe­cial­ly well if your devices can already handle modern formats and rarely need transcod­ing.

Advanced users

If your media library is growing, you want to manage multiple users, or you’d like more flex­i­bil­i­ty, cloud servers or Cloud Cubes are a great option. You can start with around 4 vCPUs, 8 GB RAM, and 240 GB of storage. That gives you enough headroom for parallel scans, metadata pro­cess­ing, larger libraries and oc­ca­sion­al transcod­ing.

The main benefit here is flex­i­bil­i­ty. You can easily scale your setup as your needs change. That’s useful if you want to start small and expand later, for example by adding more users, more libraries, or ad­di­tion­al services like a reverse proxy, mon­i­tor­ing, or backup. For advanced users, small teams, or homelab setups, this is often a com­fort­able middle-ground.

High-per­for­mance and 4K setups

If you’re working with a large 4K library, need HDR-to-SDR con­ver­sion, or want to support multiple users streaming at the same time, a small VPS will run into per­for­mance issues fairly quickly. Jellyfin points out that software-based HDR-to-SDR tone mapping can be extremely CPU-intensive. In these cases, a dedicated or bare metal server makes much more sense. You get dedicated hardware and don’t have to share CPU, RAM, or storage with other users.

For this kind of setup, plan for at least 8 CPU cores, 16 to 32 GB RAM, and fast SSD or NVMe storage. Ideally, you’ll also use an Intel or NVIDIA GPU for hardware transcod­ing.

Which operating system should you use Debian or Ubuntu?

Both Linux server dis­tri­b­u­tions work well with Jellyfin. That said, Ubuntu Server 24.04 LTS is usually the easier choice if you’re just getting started. Docker of­fi­cial­ly supports Ubuntu 24.04, and most guides are based on it. Although Debian is also very stable, Ubuntu tends to be a bit more beginner-friendly and helps you avoid some common setup issues.

Tip

If your media col­lec­tion is growing quickly, external storage can be helpful. For beginners, local SSD or NVMe storage is the easiest option. You can also use external storage such as object storage, but you’ll first need to mount it as a file system first, using a mount tool. This is because Jellyfin reads files directly from the file system and doesn’t support object storage as a native source. A good workaround is to use object storage as an archive for older media you don’t need all the time.

How to install a Jellyfin server on Ubuntu using Docker

In this example, we’ll walk you through how to install Jellyfin on Ubuntu Server 24.04 LTS using Docker Engine and the Docker Compose plugin. This approach is es­pe­cial­ly beginner-friendly because Docker keeps every­thing nicely separated and makes updates easier to manage.

Step 1: Update your Ubuntu server

Connect to your server via SSH and update your system:

sudo apt update 
sudo apt upgrade -y
bash

If needed, restart the server (you will have to log in again after rebooting):

sudo reboot
bash

This step makes sure you’re starting with up-to-date packages, the latest security updates and a clean system. If you’re working with a freshly set up Ubuntu server, this is a good first thing to do.

Step 2: Install Docker on Ubuntu 24.04

Next, install Docker using the official APT repos­i­to­ry. First, remove any older or po­ten­tial­ly con­flict­ing packages:

sudo apt remove docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc -y
bash

Then set up the official Docker repos­i­to­ry:

sudo apt update 
sudo apt install ca-certificates curl -y 
sudo install -m 0755 -d /etc/apt/keyrings 
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 
sudo chmod a+r /etc/apt/keyrings/docker.asc 
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF 
Types: deb 
URIs: https://download.docker.com/linux/ubuntu 
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") 
Components: stable 
Signed-By: /etc/apt/keyrings/docker.asc 
EOF 
sudo apt update
bash

Now install Docker Engine, Buildx, and the Compose plugin using:

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
bash

Then, check Docker is running:

sudo systemctl status docker
bash

You should see active (running):

Image: Docker status display
The “active” status confirms Docker is running.

Run a quick test to make sure every­thing works:

sudo docker run hello-world
bash

The output should look like this:

Image: Docker Hello World
The Hello World message tells you Docker is installed and working as expected.

Step 3: Use Docker without sudo

By default, Docker commands require sudo. If you prefer, you can add your user to the docker group:

sudo usermod -aG docker $USER 
newgrp docker
bash
Note

The Docker doc­u­men­ta­tion points out that anyone in the docker group ef­fec­tive­ly has root-level access. That’s fairly normal on a personal server, but it’s still something to keep in mind. If other users also have access to your server, this can quickly turn into a security issue.

Step 4: Create the folder structure

Now every­thing’s set up, let’s create the folder structure for your Jellyfin server. To keep things organized, it helps to separate your con­fig­u­ra­tion, cache and media files. Go ahead and create the following folders on your host system (you can structure them dif­fer­ent­ly if you prefer):

mkdir -p ~/jellyfin/config 
mkdir -p ~/jellyfin/cache 
mkdir -p ~/media/movies 
mkdir -p ~/media/tvshows 
mkdir -p ~/media/music 
mkdir -p ~/media/photos
bash

config is where Jellyfin stores its con­fig­u­ra­tion, users and database. cache is used for temporary data. The media files them­selves are stored in separate folders.

Step 5: Determine the user’s UID and GID

Jellyfin can run inside the container using the user and group ID of your Linux user. This makes it easier to handle file per­mis­sions. To get both values, run:

id
bash

You’ll see a set of numbers in the output: the UID and GID values. Make a note of them. You’ll need them in the next step for the Compose file.

Step 6: Create the Docker Compose file for Jellyfin

Go to your working directory and create a file called docker-compose.yml. In this example, we’ll use the nano editor, but you can create the file however you like.

mkdir -p ~/jellyfin 
cd ~/jellyfin 
nano docker-compose.yml
bash

Now paste the following content into the file:

services: 
    jellyfin: 
        image: jellyfin/jellyfin 
        container_name: jellyfin 
        user: "1000:1000" 
        ports: 
            - "8096:8096/tcp" 
            - "7359:7359/udp" 
        volumes: 
            - ./config:/config 
            - ./cache:/cache 
            - type: bind 
                source: /home/YOURUSERNAME/media/movies 
                target: /media/movies 
                read_only: true 
            - type: bind 
                source: /home/YOURUSERNAME/media/tvshows 
                target: /media/tvsshows
                read_only: true 
            - type: bind 
                source: /home/YOURUSERNAME/media/music 
                target: /media/music 
                read_only: true 
            - type: bind 
                source: /home/YOURUSERNAME/media/photos 
                target: /media/photos 
                read_only: true 
        environment: 
            - JELLYFIN_PublishedServerUrl=http://YOUR-SERVER-IP:8096 
        restart: unless-stopped
yaml

Then, update the following values in the file:

  • replace 1000:1000 with your actual UID:GID from the previous step
  • YOURUSERNAME with your Linux username
  • YOUR-SERVER-IP with your server’s IP address
Tip

The media folders are mounted as read_only: true. This means Jellyfin server can read your files, but it can’t change them. For a personal media server, that’s both safer and more practical.

Step 7: Start the Jellyfin container

Now you can start the container you just created using Docker Compose. It will run in the back­ground:

sudo docker compose up -d
bash

Then check if the container is up and running:

sudo docker compose ps
bash
Image: Display after Docker compose
The terminal output shows that the Jellyfin container is up and running.

Step 8: Open Jellyfin in your browser

Now open your server’s address in your browser using the default Jellyfin port:

http://SERVER-IP:8096

The setup wizard will start au­to­mat­i­cal­ly in your browser.

Note

If you’re using an IONOS server, you’ll first need to configure the firewall in the Cloud Panel to allow TCP con­nec­tions on port 8096.

Step 9: Set up Jellyfin using the wizard

Jellyfin will guide you through the basic setup in your browser. Start by choosing your language, then create an admin account with a secure password. After that, set up your libraries. For example, select “Movies,” “TV shows,” “Music,” and “Photos” and point Jellyfish to the folders you mounted in the container, such as /media/movies or /media/tvshows, as in our example. Once that’s done, Jellyfin will au­to­mat­i­cal­ly pull in metadata, cover art and ad­di­tion­al data, as long as your server has an internet con­nec­tion.

Image: Screenshot of Jellyfin in the browser
When you open Jellyfin in your browser, the setup wizard walks you through every­thing step by step.

Step 10: Keep your media organized

For Jellyfin to recognize your files correctly, your media should be clearly named and well organized. For movies, it helps to store each title in its own folder. For TV shows, organize them by season. Music works best when your tags are clean and con­sis­tent. The more struc­tured your folders are, the less manual work you’ll have later when it comes to metadata and matching. It’s not something you have to do, but it def­i­nite­ly makes things easier later on.

Keep the following security tips for your Jellyfin server in mind:

  • Use HTTPS: If you want to access Jellyfin over the Internet, make sure to secure it with HTTPS. The easiest way to do this is with a reverse proxy and a free TLS cer­tifi­cate.
  • Avoid direct internet exposure: Try not to open the Jellyfin port directly on your router. Instead, use a reverse proxy or a VPN.
  • Use strong passwords: Set a secure password for the admin account and create separate user accounts for others.
  • Keep your system up to date: Regularly update your Ubuntu system, Docker, and the Jellyfin image.
  • Mount media folders as read-only: If you’re running Jellyfin in Docker, it’s a good idea to mount your media folders as read-only (read_only) to avoid ac­ci­den­tal changes to your files.

How to connect Jellyfin to a domain

If you want to access Jellyfin through a domain like jellyfin.yourdomain.com, you’ll need three things:

  • A domain or subdomain
  • A DNS record that points to your server’s public IP address
  • A reverse proxy with HTTPS. Jellyfin specif­i­cal­ly rec­om­mends Caddy

Step 1: Set the DNS record for the subdomain

Create an A record with your domain provider, for example:

jellyfin.yourdomain.com -> YOUR-PUBLIC-IP

If you’re using IPv6, you’ll also need to create an AAAA record. For automatic HTTPS with Caddy, the DNS record must already point to your server correctly.

Note

For your Jellyfin server to be ac­ces­si­ble over HTTPS later on, ports 80 (HTTP) and 443 (HTTPS) need to be open on your server or in your firewall. The reverse proxy uses these ports to handle incoming requests and issue TLS cer­tifi­cates.

Step 2: Install Caddy

Jellyfin rec­om­mends Caddy because it makes setting up HTTPS really straight­for­ward. To install Caddy on Ubuntu, use the official package method and run the following commands:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl 
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg 
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list 
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg 
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list 
sudo apt update 
sudo apt install caddy -y
bash

Once the in­stal­la­tion is complete, Caddy starts au­to­mat­i­cal­ly as a system service.

Step 3: Configure Caddy

Next, update the Caddy con­fig­u­ra­tion. We’ll use nano again here, but any text editor will work as well:

sudo nano /etc/caddy/Caddyfile
bash

Add the following:

jellyfin.yourdomain.com { 
reverse_proxy 127.0.0.1:8096 
}

Save the file, then check the con­fig­u­ra­tion:

sudo caddy validate --config /etc/caddy/Caddyfile
bash

If every­thing looks good, reload Caddy:

sudo systemctl reload caddy
bash

Step 4: Bind Jellyfin locally to Caddy only

Once your domain is working, you should update your Compose file so the Jellyfin no longer listens publicly on port 8096. To do this, open ~/jellyfin/docker-compose.yml again and replace the port section with:

ports: 
    - "127.0.0.1:8096:8096/tcp" 
    - "7359:7359/udp"
yaml

Then restart the container:

sudo docker compose up -d
bash

Jellyfin will now only be ac­ces­si­ble locally, while Caddy handles the secure public con­nec­tion. Finally, add the proxy as a Known Proxy in Jellyfin’s network settings. This allows Jellyfin to correctly process the headers set by the proxy. You’ll find this option in the web interface under Dashboard → Net­work­ing → Known Proxies.

Reviewer

Go to Main Menu