Install Jellyfin on VPS: Build Your Own Streaming Server
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
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU (for software transcoding) |
| RAM | 2 GB | 4–8 GB |
| Disk | 20 GB OS | SSD + external/NFS for media |
| OS | Ubuntu 22.04+ | Ubuntu 24.04 / Debian 12 |
| Port | 8096 (HTTP) | 443 (HTTPS via Nginx) |
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
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:
- Select Language — choose your preferred UI language
- Create Admin Account — set a strong username and password
- Add Media Library — select content type (Movies / TV Shows / Music) and the file path
- Set Metadata Language — choose language for pulling movie metadata
- 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:
- Base URL: Leave empty if using the root domain
- Known Proxies:
127.0.0.1 - Disable Allow remote connections to Jellyfin to force all traffic through Nginx
Step 10: Install Client Apps
| Platform | App | Download |
|---|---|---|
| Web Browser | Built-in WebUI | No install needed |
| Android | Jellyfin for Android | Google Play Store |
| iOS / iPadOS | Swiftfin | App Store |
| Apple TV | Swiftfin | App Store |
| Android TV | Jellyfin for Android TV | Google Play |
| Windows | Jellyfin Media Player | jellyfin.org |
| Kodi | Jellyfin for Kodi Plugin | Kodi 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
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.