How to Install Jellyfin on a VPS with Docker

Paying for Netflix, Plex Pass, and Emby Premier all at once adds up fast. There’s a better way. Jellyfin is a completely free, open-source media server that gives you full control over your movies, TV shows, music, and photos, without a subscription fee or a corporation harvesting your watch history. Host it on a VPS and your entire library becomes accessible from anywhere in the world.

This guide walks you through installing Jellyfin on an Ubuntu VPS using Docker, with Nginx handling SSL termination so your server looks and behaves like a professional streaming platform. By the end, you’ll have a production-ready media server with HTTPS, automatic certificate renewal, and a clean URL.

What Is Jellyfin?

Jellyfin is the community-built fork of Emby that went fully open-source in 2018. It runs on virtually anything, a Raspberry Pi in your closet, a beefy dedicated server, or a VPS in the cloud, and streams to clients on Android, iOS, Roku, Apple TV, Fire TV, and every major web browser. Think of it as your own private Netflix, except you own the content and the platform.

Unlike Plex, Jellyfin never requires a paid tier to unlock local streaming or remote access. Hardware transcoding, live TV, plugins, and multi-user support are all included for free. The trade-off is that you manage the infrastructure yourself, which is exactly what this guide covers.

Prerequisites

Before diving in, make sure your server and local machine meet these requirements.

Requirement Minimum Recommended
OS Ubuntu 22.04 LTS Ubuntu 24.04 LTS
CPU 2 vCPU 4 vCPU
RAM 2 GB 4 GB
Storage 20 GB (OS + app) 50 GB+ (plus media volume)
Network Any broadband VPS 1 Gbps port speed
Domain Required for SSL Subdomain (e.g., jellyfin.yourdomain.com)
Access Root or sudo SSH access Non-root sudo user

A quick note on storage: Jellyfin itself is lightweight. Your media library is not. Plan your disk allocation accordingly, and consider mounting a separate block storage volume for media rather than cramming everything onto the OS drive.

Step 1: Update Your System

Start clean. Refresh your package index and apply any pending upgrades before touching anything else.

sudo apt update && sudo apt upgrade -y

Once that finishes, install a few essential packages Jellyfin’s setup process depends on.

sudo apt install -y curl gnupg2 ca-certificates lsb-release apt-transport-https software-properties-common

Step 2: Install Docker Engine

Always install Docker from Docker’s official repository, not Ubuntu’s default package manager. The version in the Ubuntu repos lags well behind and can cause compatibility headaches down the road.

Add Docker’s GPG key and repository:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] /
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | /
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Now install Docker Engine and the Compose plugin:

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

Add your user to the docker group so you don’t need sudo for every command:

sudo usermod -aG docker $USER
newgrp docker

Confirm everything installed correctly:

docker --version
docker compose version

Step 3: Create the Jellyfin Directory Structure

Organization matters here. Jellyfin needs dedicated directories for its configuration, cached metadata, and your actual media files. Keeping them separate makes backups and upgrades far simpler later.

sudo mkdir -p /opt/jellyfin/{config,cache,media}
sudo chown -R $USER:$USER /opt/jellyfin

If your media lives on a separate mounted volume, adjust the media path to point there instead. For example, if you’ve mounted a block storage volume at /mnt/media, you’d bind that path directly in the Compose file below.

Step 4: Write the Docker Compose File

Create a project directory and open the Compose file:

mkdir -p ~/jellyfin && nano ~/jellyfin/docker-compose.yml

Paste in the following configuration:

services:
  jellyfin:
    image: jellyfin/jellyfin:10
    container_name: jellyfin
    restart: unless-stopped
    ports:
      - "127.0.0.1:8096:8096"
    volumes:
      - /opt/jellyfin/config:/config
      - /opt/jellyfin/cache:/cache
      - /opt/jellyfin/media:/media
    environment:
      - JELLYFIN_PublishedServerUrl=https://jellyfin.yourdomain.com

A few things worth noting about this configuration. The port binding 127.0.0.1:8096:8096 is intentional, it locks Jellyfin to the loopback interface so it’s never exposed directly to the internet. Nginx will proxy all public traffic through port 443. Replace jellyfin.yourdomain.com with your actual domain. And always verify the jellyfin/jellyfin:10 tag is current on Docker Hub before deploying.

Step 5: Start the Jellyfin Container

From the project directory, bring the stack up:

cd ~/jellyfin
docker compose up -d

Give it 10 to 20 seconds to initialize, then confirm the container is running:

docker compose ps

You should see jellyfin listed with a status of Up. If something looks off, check the logs:

docker compose logs -f jellyfin

Step 6: Configure UFW Firewall

Open only the ports your server actually needs. HTTP and HTTPS are required for Nginx. SSH keeps you connected. Everything else stays closed.

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Verify the rules look right:

sudo ufw status

You should see OpenSSH and Nginx Full listed as ALLOW. Notice that port 8096 does not appear here. That’s correct. Jellyfin listens only on localhost, so it never needs a firewall rule of its own.

Step 7: Install and Configure Nginx

Install Nginx if it isn’t already present:

sudo apt install -y nginx

Create a new server block for Jellyfin:

sudo nano /etc/nginx/sites-available/jellyfin

Paste the following configuration, replacing jellyfin.yourdomain.com with your actual domain:

server {
    listen 80;
    server_name jellyfin.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name jellyfin.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/jellyfin.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jellyfin.yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Required for Jellyfin WebSocket support
    proxy_http_version 1.1;

    location / {
        proxy_pass http://127.0.0.1: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;

        # WebSocket headers
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Disable buffering for real-time streaming
        proxy_buffering off;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

The Upgrade and Connection headers are critical for Jellyfin’s real-time features, including playback state sync, web-based clients, and the dashboard. Skipping them causes mysterious UI failures that can be maddening to debug. Similarly, proxy_buffering off prevents Nginx from buffering streaming responses, which would introduce latency and playback stalls.

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/jellyfin /etc/nginx/sites-enabled/
sudo nginx -t

You should see syntax is ok and test is successful. If not, recheck the domain names and file paths in the config.

Step 8: Obtain an SSL Certificate with Certbot

Install Certbot and its Nginx plugin:

sudo apt install -y certbot python3-certbot-nginx

Point your domain’s DNS A record at your VPS IP address before running Certbot. The certificate request will fail if DNS hasn’t propagated yet. Once you’ve confirmed the DNS is live, request your certificate:

sudo certbot --nginx -d jellyfin.yourdomain.com

Certbot will prompt for an email address, ask you to agree to the terms of service, and then automatically validate your domain and update the Nginx configuration. When it finishes, reload Nginx to apply everything:

sudo systemctl reload nginx

Certbot installs a systemd timer that automatically renews certificates before they expire. Confirm it’s active:

sudo systemctl status certbot.timer

Step 9: Complete the Jellyfin Setup Wizard

Open a browser and navigate to https://jellyfin.yourdomain.com. Jellyfin’s first-run wizard greets you immediately. Work through the following steps:

  1. Choose your language and click Next.
  2. Create an admin account. Use a strong password. This account controls everything.
  3. Add a media library. Click the plus icon, select your content type (Movies, TV Shows, Music, etc.) and point it to the corresponding path inside /media. For example, movies stored at /opt/jellyfin/media/movies appear inside the container as /media/movies.
  4. Set your metadata language and country for accurate library scraping.
  5. Finish the wizard and log in with the admin credentials you just created.

Jellyfin starts scanning your media library immediately after setup. Depending on how large your collection is, the initial scan can take anywhere from a few minutes to a couple of hours. Let it run in the background while you configure the rest of your preferences.

Step 10: Add Media to Your Library

Getting media onto your VPS is a separate process from running Jellyfin. A few common approaches:

  • SCP or SFTP: Transfer files from your local machine directly over SSH. Slow for large libraries but works out of the box.
  • rsync: The smarter choice for ongoing transfers. It syncs incrementally, skipping files that already exist on the server.
  • Sonarr/Radarr: Pair Jellyfin with these companion apps to automate downloads and organize your library automatically as new content arrives.
  • Rclone: Mount a cloud storage bucket (Google Drive, Backblaze B2, etc.) directly as a local directory and point Jellyfin at it.

Whatever method you choose, keep your file naming consistent. Jellyfin’s metadata scrapers work best when movies follow the Movie Title (Year).ext pattern and TV episodes use Show Name/Season XX/Show Name S01E01.ext.

How to Update Jellyfin

Jellyfin releases new versions regularly. Keeping current means you get bug fixes, new codec support, and security patches. The update process takes about a minute.

Pull the latest image:

cd ~/jellyfin
docker compose pull

Recreate the container with the updated image:

docker compose up -d --force-recreate

Verify the new version is running:

docker compose logs -f jellyfin

Your configuration and media binds survive the update untouched because they live in named volumes outside the container. The update only replaces the application layer.

Troubleshooting

Jellyfin returns a 502 Bad Gateway error

A 502 almost always means Nginx can’t reach the Jellyfin container. Start by confirming the container is actually running:

docker compose ps

If the container shows as stopped or restarting, check the logs for the root cause:

docker compose logs --tail=50 jellyfin

Also confirm the port binding is correct. Run docker compose port jellyfin 8096 and verify the output shows 127.0.0.1:8096. If the container is running fine but you’re still getting 502s, double-check that the proxy_pass address in your Nginx config matches exactly.

The browser shows an SSL certificate warning

This usually means Certbot hasn’t run yet or the certificate paths in the Nginx config are wrong. Confirm your certificate exists:

sudo certbot certificates

If the certificate is listed but Nginx is still serving an invalid cert, check that the domain in the Nginx server_name directive matches the domain Certbot issued the certificate for. Reload Nginx after making any corrections: sudo systemctl reload nginx.

Media files appear in the directory but not in Jellyfin

Jellyfin won’t pick up new files automatically until it scans. Trigger a manual scan from the dashboard: go to Administration > Dashboard > Libraries and click Scan All Libraries. If files still don’t appear after scanning, confirm the file names follow Jellyfin’s expected naming conventions. Also check that the volume path in docker-compose.yml maps to the correct directory on the host.

Playback stutters or stops partway through

Playback issues on a VPS typically come from one of three places: server CPU struggling with software transcoding, insufficient upload bandwidth, or Nginx buffering responses. For CPU issues, check utilization during playback with docker stats jellyfin. If it’s pegged at 100%, consider enabling hardware transcoding (requires a VPS with GPU access) or encouraging clients to play files in their native format to avoid transcoding altogether. For buffering, confirm proxy_buffering off is present in your Nginx config and reload Nginx after adding it.

The Jellyfin container keeps restarting

A crash-looping container usually points to a permission problem or a corrupt configuration file. First, inspect the logs:

docker compose logs --tail=100 jellyfin

If you see permission denied errors, fix ownership on the config and cache directories:

sudo chown -R 1000:1000 /opt/jellyfin/config /opt/jellyfin/cache

If the logs point to a corrupted config, you can safely delete the contents of /opt/jellyfin/config and re-run the setup wizard. Your media files are untouched because they live in a separate directory.

What to Build Next

A running Jellyfin server is just the beginning. Here’s where most people take it from a functional install to a genuinely impressive self-hosted media stack.

  • Add Sonarr and Radarr to automate TV show and movie downloads. Both integrate directly with Jellyfin and keep your library organized without any manual effort.
  • Set up Jellyseerr, a request management frontend that lets other users on your server request new content without needing admin access.
  • Enable hardware transcoding if your VPS provider offers GPU instances. Jellyfin supports Intel Quick Sync, NVIDIA NVENC, and AMD AMF, all of which offload transcoding from the CPU dramatically.
  • Configure scheduled library scans under Administration so newly added files appear automatically on a regular interval.
  • Install the Jellyfin app on your television, phone, or tablet and enjoy your library from anywhere without paying a dime in licensing fees.

Running your own media server takes a bit of setup, but what you get in return is complete ownership. No one can raise your subscription price, remove titles from your library, or restrict how many devices you stream to at once. Your content, your rules, your server.

The post How to Install Jellyfin on a VPS with Docker appeared first on GreenGeeks.

版权声明:
作者:dingding
链接:https://www.techfm.club/p/237260.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>