Jellyfin media server running in Docker on a VPS behind HTTPS
Self-hosting

Jellyfin Docker Setup: Self-Host Your Media Server on a VPS

Streaming services keep reshuffling their catalogues, raising prices and spreading your favourite shows across more subscriptions every year. Meanwhile, the films you ripped from your own discs, the family videos and the music collection you spent years tagging sit on a drive in a drawer. Jellyfin turns that pile of files into your own private streaming service, with posters, plot summaries, “continue watching” and apps for TVs and phones. The only thing it costs is the server it runs on.

Jellyfin is free and open source software: no account to create, no telemetry, no premium tier hiding the useful features. The project lives at jellyfin.org and the code at github.com/jellyfin/jellyfin . In this jellyfin docker setup guide we run it on a VPS behind HTTPS with automatic certificates, organize movie, series and music libraries so the metadata matches the first time, create accounts for family and friends, and look honestly at the one job that can make a VPS sweat: transcoding.

What Jellyfin is, and the few concepts you need

A media server is a program that scans folders of video and audio files, recognizes what each file is, downloads metadata for it (posters, descriptions, cast, episode titles) from public databases such as TheMovieDb and MusicBrainz, and then streams the content to client apps. Jellyfin does all of that and also remembers watch progress per user, so everyone in the family has their own “continue watching” row.

We will run it with Docker. A container is an isolated package holding an application plus everything it needs (libraries, a runtime, its own build of ffmpeg), so it behaves the same on any Linux server and does not scatter files across your system. A container is started from an image, a read-only template published by the project. Docker Compose lets us describe several containers in a single YAML file and start them with one command, which makes the whole setup easy to read, back up and rebuild.

A port is a numbered door on the server through which a network service listens. Jellyfin listens on port 8096 using plain, unencrypted HTTP. We will not expose that port to the internet. Instead, a reverse proxy called Caddy listens on the standard web ports 80 and 443, obtains a free TLS certificate from Let’s Encrypt automatically, and forwards encrypted visitors to Jellyfin internally. The result is a clean address like https://media.example.com with the padlock in the browser.

Finally, transcoding means converting a video on the fly into a format, resolution or bitrate that the client device can play. It is convenient, but it is pure CPU work, and on a VPS without a graphics card it is the main thing to plan for. Step 8 covers it in detail.

Why put Jellyfin on a VPS

A home server is cheap on storage but usually sits behind a residential connection, often behind carrier-grade NAT and with a weak upload speed, which is exactly the direction streaming needs. A VPS in a data center is always on, has a public IP and plenty of bandwidth, so your library works the same from a hotel, a phone on 4G or a relative’s smart TV. The tradeoffs are honest ones: disk space costs more per GB than a hard drive at home, and standard VPS plans have no GPU. A VPS shines with a curated library and a handful of viewers; multi-terabyte 4K collections belong on a dedicated server.

How the pieces fit together

How the Jellyfin stack fits togetherViewersbrowser, TV,phone appsYour VPS: Debian 13 / Ubuntu 24.04Firewallufwopen ports22, 80, 443Docker network 172.30.0.0/24CaddyHTTPS :443auto TLS cert172.30.0.10Jellyfinport 8096ffmpeg inside172.30.0.20Volumes/config/cache/srv/mediaread-onlyPort 8096 is bound to 127.0.0.1 only:reach it directly only through an SSH tunnel
Figure 1: viewers only ever reach port 443. Caddy handles HTTPS and forwards to Jellyfin over a private Docker network; media is mounted read-only.

Viewers only ever talk to port 443. The firewall lets through SSH, HTTP and HTTPS and nothing else, Caddy terminates TLS and forwards requests over a private Docker network, and Jellyfin keeps its database in /config, writes temporary transcode segments to /cache and reads your media from a read-only folder. That last detail means a bug or a compromised account can never delete your films.

What you need

  • A VPS with Debian 13 or Ubuntu 24.04, and a regular user with sudo rights. A KVM VPS is recommended. 2 vCPU and 2 GB of RAM are enough for direct play; plan for 4 vCPU or more if you expect transcoding.
  • Enough disk for your library. A 1080p film is usually 4 to 15 GB, a season of a series 10 to 40 GB, an album in FLAC around 300 to 500 MB.
  • A domain or subdomain you control, for example media.example.com, where you can create a DNS record.
  • Ports 80 and 443 reachable from the internet (they are, on a fresh DreamServer VPS).
  • Media you have the right to use: rips of discs you own, home videos, public domain or Creative Commons works.
  • About 30 to 45 minutes, plus upload time for your files.

A note on LXC: Docker inside an unprivileged LXC container needs nesting enabled on the host, can run into storage driver and permission quirks, and kernel tunables such as inotify limits cannot be changed from inside the container. Hardware transcoding through /dev/dri is not available there at all. If you have the choice, use a KVM VPS for this guide.

Jellyfin on a VPS in 8 steps1PrepareDocker, ufw, DNS2Foldersmedia layout3ComposeJellyfin + Caddy4CaddyfileHTTPS proxy5Startsetup wizard6Librariesmetadata match7Usersaccess, limits8TranscodingCPU tuning
Figure 2: the procedure in eight steps. Steps 1 to 5 install the stack, Steps 6 to 8 configure Jellyfin; the numbers match the Step 1 to Step 8 headings below.

Step 1: Prepare the server (Docker, firewall, DNS)

Start by updating the system and installing a few tools. On Ubuntu 24.04, ufw is already installed (but inactive); on Debian 13 it is not, so the command below installs it.

sudo apt update && sudo apt full-upgrade -y
sudo apt install -y curl ca-certificates ufw

If the upgrade pulled in a new kernel, reboot. On Ubuntu you can check with ls /var/run/reboot-required: if the file exists, a reboot is pending. Debian does not create that file by default, so there the simple rule is to reboot whenever a linux-image package was upgraded.

Now install Docker Engine and the Compose plugin with Docker’s official convenience script. It detects whether you run Debian or Ubuntu and adds the matching Docker repository.

curl -fsSL https://get.docker.com -o get-docker.sh
less get-docker.sh   # optional, but good practice: read what you run
sudo sh get-docker.sh
sudo docker version
sudo docker compose version

The last command should print something like Docker Compose version v2.x.x. Note the space: it is docker compose, the current v2 plugin, not the old docker-compose binary. In this guide we prefix Docker commands with sudo. You could add your user to the docker group instead, but membership in that group is equivalent to root access, so do it knowingly.

Next, the firewall. Allow SSH first, otherwise enabling the firewall can lock you out of your own server:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw enable
sudo ufw status verbose

If you moved SSH to a custom port, allow that port instead of OpenSSH. Port 443/udp carries HTTP/3, which Caddy supports out of the box. The status output should list 22, 80 and 443 as ALLOW IN.

Finally, DNS. At your registrar or DNS provider, create an A record for media.example.com pointing to the public IPv4 address of your VPS (plus an AAAA record if you use IPv6). After a few minutes, check from the server:

getent hosts media.example.com

It should print your VPS IP. Caddy needs this to be correct before it can obtain a certificate.

Step 2: Create folders and the media layout

We keep the Docker files in /opt/jellyfin and the media in /srv/media, with one folder per content type. Separating movies, shows and music is not cosmetic: Jellyfin applies different naming rules and metadata providers per library type, and mixed folders are the number one cause of wrong matches.

sudo mkdir -p /opt/jellyfin /srv/media/movies /srv/media/shows /srv/media/music
sudo chown -R "$USER":"$USER" /opt/jellyfin /srv/media
cd /opt/jellyfin
mkdir config cache
id -u; id -g

The last line prints your user ID and group ID, usually 1000 and 1000 for the first user on the VPS. Write them down: Jellyfin will run as that user, so it can read the files you upload and write its own config without running as root. We create config and cache ourselves on purpose: if Docker has to create a missing folder, it makes it owned by root, which later shows up as “permission denied” errors.

Naming that makes metadata match

Jellyfin identifies content mostly from folder and file names. Follow this layout and nearly everything matches on the first scan:

/srv/media/
  movies/
    Nosferatu (1922) [imdbid-tt0013442]/
      Nosferatu (1922).mkv
      Nosferatu (1922).en.srt
    Night of the Living Dead (1968)/
      Night of the Living Dead (1968).mp4
  shows/
    Example Show (2019)/
      Season 00/
        Example Show S00E01.mkv
      Season 01/
        Example Show S01E01.mkv
        Example Show S01E02.mkv
  music/
    Artist Name/
      Album Name (2004)/
        01 - First Track.flac
        02 - Second Track.flac
        cover.jpg

The rules behind it:

  • Movies: one folder per film, named Title (Year), with the video file named the same. The year separates remakes from originals. Adding [imdbid-tt...] or [tmdbid-...] to the folder name pins the exact match, which is handy for films with common titles.
  • Shows: Show Name (Year)/Season 01/, and every episode file contains S01E01. Specials go into Season 00. A double episode can be named S01E01-E02.
  • Subtitles: an external file with the same base name plus a language code, for example .en.srt, .ro.srt or .ro.forced.srt for forced subtitles.
  • Music: Jellyfin trusts the tags embedded in the files more than folder names. Make sure Album Artist is set, especially on compilations, otherwise one album splits into dozens. A tagger such as MusicBrainz Picard can clean up a whole collection.

To upload, use rsync from your computer (Linux, macOS or WSL on Windows):

rsync -avP "Nosferatu (1922) [imdbid-tt0013442]" youruser@your-vps-ip:/srv/media/movies/

-a preserves the folder structure, -v lists files as they go, and -P shows progress and lets you resume an interrupted transfer, which matters for 10 GB files. On Windows, any SFTP client such as WinSCP or FileZilla works with the same SSH login.

Step 3: Write the Docker Compose file

Create the Compose file in /opt/jellyfin:

cd /opt/jellyfin
nano compose.yaml

Paste the following, replacing media.example.com with your domain and 1000:1000 with the IDs you noted in Step 2:

services:
  jellyfin:
    image: jellyfin/jellyfin:10.11.0
    container_name: jellyfin
    user: "1000:1000"
    restart: unless-stopped
    environment:
      - TZ=Europe/Bucharest
      - JELLYFIN_PublishedServerUrl=https://media.example.com
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /srv/media:/media:ro
    ports:
      - "127.0.0.1:8096:8096"
    networks:
      media:
        ipv4_address: 172.30.0.20

  caddy:
    image: caddy:2.10
    container_name: caddy
    restart: unless-stopped
    depends_on:
      - jellyfin
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      media:
        ipv4_address: 172.30.0.10

networks:
  media:
    ipam:
      config:
        - subnet: 172.30.0.0/24

volumes:
  caddy_data:
  caddy_config:

Save with Ctrl+O, Enter, and exit with Ctrl+X. What each part does:

  • image: jellyfin/jellyfin:10.11.0 pins a specific version. Check the releases page and use the newest 10.11.x patch available. We avoid :latest because major upgrades (10.10 to 10.11, for example, migrated the whole database) run automatically on start and cannot be undone. With a pinned tag, you upgrade when you decide, after a backup.
  • user runs Jellyfin as your regular user instead of root.
  • ports: "127.0.0.1:8096:8096" publishes Jellyfin only on the loopback interface. This matters more than it looks: Docker writes its own firewall rules and bypasses ufw for published ports, so a plain 8096:8096 would be open to the whole internet even with ufw enabled. Binding to 127.0.0.1 keeps it private while still allowing an SSH tunnel for troubleshooting.
  • /srv/media:/media:ro shows your media inside the container at /media, read-only.
  • JELLYFIN_PublishedServerUrl tells client apps which public address to use.
  • The fixed IPs on the media network let us tell Jellyfin exactly which address is its trusted proxy in Step 5.
  • caddy_data is where Caddy stores certificates. Never delete it casually: re-requesting certificates repeatedly can hit Let’s Encrypt rate limits.

Step 4: Add HTTPS with Caddy

Create the Caddy configuration in the same folder. It must exist before the first start, otherwise Docker creates an empty folder named Caddyfile in its place.

nano /opt/jellyfin/Caddyfile
media.example.com {
    reverse_proxy jellyfin:8096
}

That is the whole file. Writing a domain name as the site address tells Caddy to obtain a Let’s Encrypt certificate, redirect HTTP to HTTPS and renew the certificate before it expires, all automatically. reverse_proxy jellyfin:8096 forwards traffic to the container named jellyfin, which Docker resolves on the private network. WebSocket connections, which Jellyfin uses for live updates and remote control between devices, are passed through without extra configuration.

Step 5: Start the stack and run the setup wizard

Start both containers in the background:

cd /opt/jellyfin
sudo docker compose up -d
sudo docker compose ps

The first run downloads the images, then ps should show jellyfin and caddy with a status of Up. Jellyfin may show (health: starting) for a minute before switching to (healthy). Now watch Caddy request the certificate:

sudo docker compose logs -f caddy

Look for a line containing certificate obtained successfully and your domain, then press Ctrl+C to stop following the log. Two quick checks:

curl -s http://127.0.0.1:8096/health; echo
curl -sI https://media.example.com | head -n 1

The first should print Healthy, the second HTTP/2 302 (a redirect to the web interface) or HTTP/2 200.

Open https://media.example.com in your browser and finish the wizard right away: until an administrator account exists, anyone who finds the address could claim the server. If you prefer to do it privately, open an SSH tunnel from your computer with ssh -L 8096:127.0.0.1:8096 youruser@your-vps-ip and browse to http://localhost:8096 instead.

The wizard asks for:

  1. The display language.
  2. The administrator username and a strong password. Avoid obvious names like admin.
  3. Media libraries: skip this for now, we add them properly in Step 6.
  4. The preferred metadata language and country.
  5. Remote access: keep Allow remote connections to this server enabled, because every viewer arrives through the proxy.

Then log in and set the trusted proxy. Open Dashboard, then Networking, find Known proxies and enter 172.30.0.10, the fixed IP of Caddy. Save, then restart Jellyfin:

sudo docker compose restart jellyfin

Without this, Jellyfin sees every viewer as coming from Caddy’s private IP: it treats everyone as being on your local network, ignores the internet bitrate limits we set in Step 7, and the logs show one useless address instead of real client IPs. Menu names can shift slightly between Jellyfin versions, but the setting is always in the networking section.

Step 6: Add libraries so metadata matches

Go to Dashboard, Libraries, Add Media Library. For each library:

  • Choose the Content type first (Movies, Shows or Music). It decides which naming rules and metadata providers apply, so it matters far more than the display name.
  • Folders: click the plus sign and enter the path as the container sees it: /media/movies, /media/shows or /media/music. Typing /srv/media/movies is a classic mistake; that path does not exist inside the container.
  • Language and country: set the preferred metadata language (English, Romanian and so on) and country, which also affects age ratings.
  • Metadata downloaders: leave the defaults: TheMovieDb and OMDb for movies and shows, MusicBrainz and TheAudioDB for music.
  • Enable real-time monitoring: keep it on, so new uploads appear without a manual scan.
  • On a VPS, turn off chapter image extraction and trickplay image extraction (the thumbnail previews shown while scrubbing). Both run ffmpeg over every video and can keep the CPU at 100% for hours on a large library. You can enable them later and let them run at night under Scheduled Tasks.

When all three libraries exist, click Scan All Libraries. Progress appears on the dashboard; a few hundred items typically take a few minutes.

If something matches wrongly, open the item, use the three-dot menu and choose Identify. Search by title and year, or paste the IMDb or TMDb ID, pick the right result and allow it to replace the existing images. Alternatively, fix the folder name and run Refresh metadata with “Replace all metadata”. The usual culprits are a missing year, release-group junk in file names, episodes without S01E01, and music compilations without an Album Artist tag.

Step 7: Create user accounts

Never share the administrator account. Go to Dashboard, Users and click the plus sign. Enter a name and password, and under Library access untick “Enable access to all libraries” if this person should only see some of them. Then open the new user and review:

  • Allow this user to manage the server: leave it unticked for everyone except you.
  • Internet streaming bitrate limit (Mbps): for example 8 for comfortable 1080p. This keeps one viewer from saturating your bandwidth and only works correctly once the known proxy from Step 5 is set.
  • Transcoding permissions: unticking “Allow video playback that requires transcoding” forces direct play for that user. They get an error on an incompatible file instead of silently pinning your CPU. Audio transcoding is cheap, so you can leave it allowed.
  • Maximum number of simultaneous sessions: useful if an account gets shared further than intended.
  • Parental control: set a maximum rating for children’s accounts.
  • Failed login attempts before lockout: a small number protects against password guessing.

For TVs, enable Quick Connect in Dashboard, General: the TV app shows a short code, and the user approves it from a phone or browser that is already logged in, so nobody types a password with a remote control. Viewers then install the official Jellyfin apps (Android, Android TV, iOS, desktop) and enter https://media.example.com as the server address.

Step 8: Tune transcoding for a VPS

Every playback falls into one of three modes, and knowing them is the key to keeping a VPS healthy:

  • Direct play: the client supports the container, video codec, audio codec and subtitles, so the file is sent as it is. CPU use is close to zero; the only limit is bandwidth.
  • Direct stream (remux): the codecs are fine but the container is not (for example MKV in some browsers), so Jellyfin repackages the streams without re-encoding them. Still cheap.
  • Transcode: the video is re-encoded in real time. A 1080p H.264 transcode in software keeps roughly 2 to 3 vCPU busy, and 4K HDR to 1080p with tone mapping can exceed 8.
Transcoding cost per stream on a VPS (approximate)2 vCPU4 vCPUDirect play~0.1Direct stream (remux)~0.2Audio transcode only~0.31080p to 720p H.264~1.51080p H.264 transcode~2.54K HDR to 1080p SDR8+02468Approx. vCPU cores busy per stream (software encoding)
Figure 3: approximate CPU cost of one stream with software encoding on typical VPS cores. Direct play is nearly free; a single 4K HDR transcode can saturate a large VPS.

Common reasons for a transcode: a video codec the client cannot decode (HEVC in many browsers, AV1 on older TVs), audio formats such as DTS or TrueHD (only the audio is converted, which is cheap), image-based subtitles (PGS, VobSub) or styled ASS subtitles that the client cannot render, so they get burned into the picture, and a file bitrate above the user’s limit.

To see what is happening, open the dashboard during playback: the active session shows whether it is direct playing or transcoding, and the info icon lists the reason. On the server, watch the container:

sudo docker stats jellyfin

In the CPU % column, 100% means one full core. Press Ctrl+C to exit. Then adjust Dashboard, Playback, Transcoding:

  • Hardware acceleration: None. Standard VPS plans have no GPU.
  • Enable throttling: on. Jellyfin pauses encoding when it is far enough ahead of playback, freeing the CPU.
  • Enable segment deletion: on, so old transcode segments do not fill /cache.
  • Encoding preset: veryfast or faster. Slower presets compress better but cost much more CPU.
  • Allow encoding in HEVC format: off. HEVC encoding in software is far too heavy for a VPS.
  • Tone mapping: leave it off on a VPS and keep SDR versions of HDR films instead.

To make sure transcoding never starves SSH and the rest of the system, cap the container’s CPU. Add one line to the jellyfin service in compose.yaml, at the same indentation as restart::

    cpus: "1.5"

Use a value below your vCPU count: about 1.5 on a 2 vCPU VPS, 3 on a 4 vCPU VPS. Docker refuses a value larger than the CPUs available. Apply the change with sudo docker compose up -d, which recreates only the modified container.

The most effective strategy, though, is to prepare files for direct play: H.264 video (HEVC only if all your viewers use TV or native apps that support it), AAC or AC3 audio, SRT subtitles, in MKV or MP4. With a library like that, even a 2 vCPU VPS can serve many simultaneous streams, limited mainly by bandwidth (roughly 8 to 10 Mbps per 1080p stream). If you genuinely need several transcodes at once or 4K, look at a dedicated server with an Intel iGPU and Quick Sync, passing /dev/dri into the container. That is not possible on a standard VPS, and not in an unprivileged LXC container either.

Updating and backing up Jellyfin

Everything that is not media lives in /opt/jellyfin/config: the database, users, watch history, settings and downloaded artwork. Back it up regularly, and keep the media itself backed up separately.

cd /opt/jellyfin
sudo docker compose stop jellyfin
sudo tar czf ~/jellyfin-config-$(date +%F).tar.gz -C /opt/jellyfin config
sudo docker compose start jellyfin

Stopping Jellyfin first guarantees a consistent copy of the database. Copy the resulting archive off the server, for example with rsync to your computer or to a storage box.

To update, read the release notes, make the backup above, change the version in the image: line, and run:

sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs -f jellyfin

The first start after a version change can take several minutes while the database is migrated, so do not interrupt it. Update the Caddy tag the same way from time to time, and remove old images with sudo docker image prune.

Troubleshooting: common mistakes

  • Caddy cannot get a certificate. Check sudo docker compose logs caddy. Usually the DNS record does not point to the VPS yet (verify with getent hosts), or port 80 is blocked. Let’s Encrypt must reach port 80 or 443 on your domain.
  • “502 Bad Gateway” in the browser. Caddy works but Jellyfin is not answering. Check sudo docker compose ps and sudo docker compose logs jellyfin; after an upgrade it may still be migrating the database.
  • “Is a directory” error for the Caddyfile. You started the stack before creating the file and Docker created a folder instead. Run sudo docker compose down, sudo rmdir Caddyfile, create the file, start again.
  • Permission denied on /config or /cache. The folders belong to another user. Fix with sudo chown -R 1000:1000 /opt/jellyfin/config /opt/jellyfin/cache, using your own IDs.
  • A library stays empty. Confirm the container sees the files: sudo docker compose exec jellyfin ls /media/movies. If the list is empty, check the path you used and that files are readable by your user.
  • Pool overlaps error on start. Another Docker network already uses 172.30.0.0/24. Pick another subnet, such as 172.31.0.0/24, and update both fixed IPs plus the Known proxies value.
  • Every viewer appears with IP 172.30.0.10. The known proxy from Step 5 is missing or Jellyfin was not restarted after setting it.
  • Buffering on some files only. It is almost always transcoding. Check the reason in the dashboard, then convert the file, add an SRT subtitle or use a native app instead of the browser.
  • New files are not detected automatically. Very large libraries can exceed the inotify watch limit. On a KVM VPS you can raise fs.inotify.max_user_watches with sysctl; in LXC, run a scheduled library scan instead.

Security notes

  • Docker bypasses ufw for published ports. Only publish 80 and 443 publicly. Anything else, like 8096, must be bound to 127.0.0.1, exactly as in our Compose file.
  • One strong administrator, everyone else a normal user. Use a long, unique password for the admin and a separate non-admin account for your own daily viewing.
  • Lock out password guessing with the per-user failed login limit, and review the activity log on the dashboard now and then.
  • Media is mounted read-only, so neither Jellyfin nor any account can delete or overwrite it.
  • Pin versions and update deliberately, after a backup. Jellyfin receives security fixes in patch releases, so do not let it fall many months behind.
  • Keep the system patched with sudo apt update && sudo apt upgrade, or enable unattended-upgrades.
  • Stay legal. Use content you own or are allowed to share, and keep the server private to your family and friends. Distributing copyrighted material publicly is illegal and against our terms of service.

So, is Jellyfin on a VPS worth it?

If you want your own streaming service, reachable from anywhere over HTTPS, with proper posters and per-person watch history, and you are willing to spend half an hour on setup and a little care on file formats, a jellyfin docker setup on a VPS is one of the most satisfying self-hosting projects you can do. Keep your library direct-play friendly and a modest VPS will serve the whole family; when transcoding demand grows, that is the moment to move to more cores or a dedicated server with a GPU.

You can start this stack on a VPS with us in a few minutes, on hardware in our own data center in Romania. If you would rather have someone else handle the setup, updates, backups and security, our server administration team can build and maintain it for you.

FAQ

How much CPU and RAM does Jellyfin need on a VPS?

For direct play, 2 vCPU and 2 GB of RAM are enough for a family-sized library and several viewers. Jellyfin itself uses a few hundred MB of RAM, plus more during library scans. Transcoding is the real cost: count roughly 2 to 3 vCPU per simultaneous 1080p transcode.

Can I use hardware transcoding on a VPS?

Generally no, because standard VPS plans have no GPU. Hardware transcoding needs a physical GPU, such as an Intel iGPU with Quick Sync, exposed to the container through /dev/dri. That is possible on a dedicated server, but not on a standard VPS or inside an unprivileged LXC container.

Is Jellyfin a good Plex alternative?

Yes, for most home use. Jellyfin is fully free and open source, needs no online account and puts no features behind a subscription. Plex has a more polished ecosystem on some smart TVs; Jellyfin has official apps for the main platforms and works in any modern browser.

How many people can watch at the same time?

With direct play, the limit is bandwidth: roughly 8 to 10 Mbps per 1080p stream. With transcoding, the limit is CPU, often only one or two streams on a small VPS. That is why per-user bitrate limits and direct-play friendly files matter.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX