Jellyfin Media Server on Linux VPS

Install Jellyfin on VPS: Build Your Own Streaming Server

Published: September 13, 2026 · 12 min read · Ubuntu 24.04 / Debian 12

Jellyfin is a 100% free, open-source media server that lets you build your own private Netflix on your VPS. It streams movies, TV shows, music, and photos to any device — web browser, Android, iOS, or Smart TV — with no subscription fees and no DRM restrictions.

Unlike Plex, Jellyfin requires no account creation, sends no metadata to the cloud, and has no hardware transcoding paywall. Every feature is available from day one at no cost.

Server Requirements

ResourceMinimumRecommended
CPU2 vCPU4 vCPU (for software transcoding)
RAM2 GB4–8 GB
Disk20 GB OSSSD + external/NFS for media
OSUbuntu 22.04+Ubuntu 24.04 / Debian 12
Port8096 (HTTP)443 (HTTPS via Nginx)
Tip: If your VPS has no GPU, pre-encode your video files to H.264 MP4. Direct play requires zero CPU — Jellyfin passes the stream straight to the client without any transcoding.

Step 1: Update OS and Install Dependencies

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl apt-transport-https gnupg

Step 2: Add Jellyfin Repository and Install

# Add Jellyfin GPG key
curl -fsSL https://repo.jellyfin.org/ubuntu/jellyfin_team.gpg.key \
  | sudo gpg --dearmor -o /usr/share/keyrings/jellyfin.gpg

# Add repository (Ubuntu 24.04 uses "noble")
echo "deb [signed-by=/usr/share/keyrings/jellyfin.gpg] \
  https://repo.jellyfin.org/ubuntu noble main" \
  | sudo tee /etc/apt/sources.list.d/jellyfin.list

# Install Jellyfin
sudo apt update && sudo apt install -y jellyfin
Note: For Debian 12 (Bookworm), replace noble with bookworm in the repository line above.

Step 3: Start and Enable Jellyfin Service

sudo systemctl enable jellyfin
sudo systemctl start jellyfin
sudo systemctl status jellyfin

Jellyfin listens on port 8096 (HTTP) and 8920 (internal HTTPS). Access the web UI at http://YOUR-SERVER-IP:8096 for initial setup.

Step 4: Open Firewall Port Temporarily

# Open port 8096 temporarily for the setup wizard
sudo ufw allow 8096/tcp
sudo ufw reload

Step 5: Complete the Setup Wizard

Open your browser at http://YOUR-SERVER-IP:8096 and follow these steps:

  1. Select Language — choose your preferred UI language
  2. Create Admin Account — set a strong username and password
  3. Add Media Library — select content type (Movies / TV Shows / Music) and the file path
  4. Set Metadata Language — choose language for pulling movie metadata
  5. Click Finish — Jellyfin will automatically scan your media library

Step 6: Prepare Media Directories

# Create media storage folders
sudo mkdir -p /media/jellyfin/{movies,tvshows,music,photos}

# Grant Jellyfin user access
sudo chown -R jellyfin:jellyfin /media/jellyfin
sudo chmod -R 755 /media/jellyfin

Recommended file structure that Jellyfin reads best:

/media/jellyfin/
├── movies/
│   ├── Inception (2010)/
│   │   └── Inception (2010).mp4
│   └── Dune Part Two (2024)/
│       └── Dune Part Two (2024).mkv
└── tvshows/
    └── Breaking Bad/
        ├── Season 01/
        │   ├── S01E01 - Pilot.mkv
        │   └── S01E02 - Cat's in the Bag.mkv
        └── Season 02/

Step 7: Configure Nginx Reverse Proxy + SSL

Install Nginx and Certbot:

sudo apt install -y nginx certbot python3-certbot-nginx

Create the Nginx config for Jellyfin:

sudo nano /etc/nginx/sites-available/jellyfin
server {
    listen 80;
    server_name jellyfin.yourdomain.com;

    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 support for real-time updates
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Large file streaming timeouts
        proxy_read_timeout 7200;
        proxy_send_timeout 7200;
        client_max_body_size 20G;
    }
}
# Enable the config
sudo ln -s /etc/nginx/sites-available/jellyfin /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

# Issue free SSL certificate
sudo certbot --nginx -d jellyfin.yourdomain.com

Step 8: Close Direct Port Access (Security Hardening)

# After setup is complete, block direct access to port 8096
sudo ufw delete allow 8096/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload

Step 9: Configure Jellyfin Networking Settings

Go to Admin Dashboard → Networking and configure:

Step 10: Install Client Apps

PlatformAppDownload
Web BrowserBuilt-in WebUINo install needed
AndroidJellyfin for AndroidGoogle Play Store
iOS / iPadOSSwiftfinApp Store
Apple TVSwiftfinApp Store
Android TVJellyfin for Android TVGoogle Play
WindowsJellyfin Media Playerjellyfin.org
KodiJellyfin for Kodi PluginKodi Add-on

Verify Jellyfin Is Running Correctly

# Check service status
sudo systemctl status jellyfin

# Follow logs in real time
sudo journalctl -u jellyfin -f

# Confirm port 8096 is listening
sudo ss -tlnp | grep 8096

# Test the public API endpoint
curl -s http://localhost:8096/System/Info/Public | python3 -m json.tool

# Verify Nginx proxy is working
curl -I https://jellyfin.yourdomain.com
Tip: Run curl http://localhost:8096/health — a response of Healthy confirms the Jellyfin API is responding. You can monitor this endpoint with Uptime Kuma for automated alerting.

Troubleshooting Common Issues

Error: "This server's certificate is not trusted"

This happens when a client connects directly via IP using a self-signed certificate. Fix it by connecting through your domain name that has a Let's Encrypt certificate instead.

# Verify certbot certificate renewal
sudo certbot renew --dry-run

# List all issued certificates
sudo certbot certificates

Error: "Playback Error" or Video Stalls Mid-Stream

Usually caused by proxy timeout values that are too short. Check your Nginx config for these settings:

# In /etc/nginx/sites-available/jellyfin, ensure these are present:
proxy_read_timeout 7200;
proxy_send_timeout 7200;
client_max_body_size 20G;

# Reload Nginx after editing
sudo nginx -t && sudo systemctl reload nginx

Error: "Permission denied" During Media Scan

The Jellyfin service user cannot read the media directory. Fix the ownership and permissions:

# Fix ownership for the Jellyfin user
sudo chown -R jellyfin:jellyfin /path/to/media
sudo chmod -R 755 /path/to/media

# Alternatively, add jellyfin to the group that owns the files
sudo usermod -aG media jellyfin
sudo systemctl restart jellyfin

Error: Jellyfin Consuming Excessive RAM (>2GB)

Multiple simultaneous software transcodes drain memory fast. Limit concurrent transcodes and cap memory usage via systemd:

# In Admin > Playback, reduce "Simultaneous transcodes limit" to 2

# Optionally cap memory via systemd override
sudo systemctl edit jellyfin
[Service]
MemoryMax=3G

Error: "404 Not Found" After Nginx Setup

Mismatch between the Base URL setting in Jellyfin and your Nginx config is the most common cause:

# In Jellyfin Admin > Networking: Base URL must be empty for root /
# If using a subdirectory like /jellyfin, set it in both places

# Check Nginx config syntax
sudo nginx -t
sudo tail -20 /var/log/nginx/error.log

Frequently Asked Questions

How is Jellyfin different from Plex?

Jellyfin is 100% open-source and free with no mandatory account, no feature paywalls, and full hardware transcoding support at no cost. Plex requires a Plex Pass subscription for hardware transcoding, offline sync, and live TV DVR features. Jellyfin is the better choice if privacy is a priority since no data ever leaves your server.

Can a small VPS (2GB RAM) run Jellyfin well?

Yes, if your media files are already encoded as H.264 MP4 and clients support direct play. In that case Jellyfin uses only 300–500 MB RAM with near-zero CPU. Problems arise only when software transcoding is needed — encode your files correctly before uploading and you will rarely hit resource limits.

Can Jellyfin use S3-compatible storage (Wasabi, Backblaze B2)?

Yes — mount the S3 bucket using rclone mount or s3fs, then point a Jellyfin library at the mount point. Jellyfin treats it as a local folder. The first metadata scan may be slower due to network latency, but streaming performance is generally fine for most use cases.