Why Host Your Media Server on a VPS
A self-hosted media server on a VPS makes you the owner of your catalog: movies, TV shows, music, and photos stay accessible over HTTPS from a browser, a Smart TV, or a smartphone, with no imposed quota and no analysis of your habits.
Unlike a home machine, a VPS has the stable upload bandwidth essential for remote streaming, a fixed IP to set up a domain, and 24/7 availability with no added electricity cost. Your media library no longer depends on your home connection or hardware.
Jellyfin is entirely free (GPL license) with no paid features whatsoever; Plex offers a more polished client experience but reserves hardware transcoding for Plex Pass and, since April 29, 2025, remote playback of your personal videos for Plex Pass or Remote Watch Pass. The choice therefore comes down to total freedom versus preconfigured convenience.
What You Gain by Hosting It Yourself
- Remote access over HTTPS without going through an external relay or exposing your content to a third party
- No limit on accounts, simultaneous streams, or library size
- On-the-fly transcoding driven by your own VPS resources (CPU or GPU)
- Subtitles, metadata, and cover art fetched automatically and stored on your own server
- With Jellyfin: zero paid features, no telemetry, auditable source code
- Trivial backup and migration: everything fits in a Docker volume and a media folder
- DLNA support (through Jellyfin's official plugin) to stream to TVs and network players without a dedicated client
Prerequisites for a VPS Media Server
For direct streaming (remux without re-encoding), a VPS with 2 vCPUs and 2 GB of RAM is enough. As soon as you enable software transcoding of a 1080p stream, count on 4 vCPUs and 4 GB; for several simultaneous 4K streams, aim for 6 to 8 vCPUs. Hardware transcoding (QSV, NVENC) remains rare on shared VPSes: favor files already compatible with your clients to stay in Direct Play.
On the storage side, plan for a generous volume and ideally a fast disk for metadata. You'll need Docker and Docker Compose installed, a subdomain (e.g. media.your-domain.com) pointing to the VPS IP, and a reverse proxy for TLS.
Deploy Jellyfin (or Plex) with Docker and a Reverse Proxy
Prepare the Directory Tree and Volumes
On the VPS, create
mkdir -p /srv/jellyfin/{config,cache}and a media folder/srv/media/{films,series,musique}. These paths will be mounted as volumes to persist the configuration and cleanly separate content from the database.Write the docker-compose.yml
Declare the
jellyfin/jellyfinservice (orplexinc/pms-docker), mount./config,./cache, and/srv/mediaread-only, and expose port8096(Jellyfin) or32400(Plex) locally only:127.0.0.1:8096:8096. The reverse proxy will handle public exposure.Start the Container
Run
docker compose up -d, then followdocker compose logs -f. Reach the setup wizard through an SSH tunnelssh -L 8096:127.0.0.1:8096 user@vpsto create the admin account before any public exposure.Configure Transcoding
In the Jellyfin interface, under Dashboard → Playback → Transcoding, keep the default transcoding path (
/cache/transcodesin the official image): these temporary files don't belong in./config, which you back up. If your VPS is CPU-limited, favor Direct Play and, in each user's profile (Dashboard → Users), remove the permission to play video that requires transcoding: incompatible files will fail instead of saturating the server. For Plex, the equivalent option, "Disable video stream transcoding", is under Settings → Server → Transcoder.Set Up the Reverse Proxy and SSL
Configure Caddy or Nginx to proxy
media.your-domain.comto127.0.0.1:8096. With Caddy, two lines are enough and the Let's Encrypt certificate is automatic; remember to forward the WebSocket headers for real-time playback. Note for Jellyfin 12: the legacy/emby/and/mediabrowser/prefixes have been removed — if you configured your reverse proxy on those paths, update it to point to the root/.Secure Access with Jellyfin Authentication
Jellyfin natively handles authentication: enforce long passwords and, in each user's profile (Dashboard → Users), set the maximum number of failed login attempts before lockout. For Plex behind a reverse proxy, declare the public URL under Settings → Server → Network ("Custom server access URLs"); remote playback of your videos also requires a Plex Pass on the server admin account, or a Remote Watch Pass on each viewer's account. Never open port
8096or32400directly on the firewall: only ports 80/443 should be accessible.Index Your Libraries
In the interface, add the
/media/filmsand/media/seriesfolders, choose the metadata agent, and start the scan. Verify that a file plays in Direct Play from an external client to confirm that the proxy and codecs are correct.Back Up the Configuration
Backing up Jellyfin comes down to two elements: the
./configfolder (database, plugins, preferences) and your media files. Automate a dailytar -czf jellyfin-config-$(date +%Y%m%d).tar.gz ./configand sync to remote storage. For Plex, thePlex Media Server/folder is the equivalent. Test the restore procedure on a test environment before you need it.
Jellyfin 12: What Changes and How to Migrate
Jellyfin 12.0 was released on September 7, 2026; the branch is now at 12.4, published in October 2026. The jump from 10.11.x to 12 comes from a numbering change: the project dropped the leading "10.", and 12.0 is the release that would have been called 10.12.0. It still introduces breaking changes.
Removal of legacy API prefixes. The /emby/ and /mediabrowser/ routes have been permanently removed: old third-party clients and scripts that relied on them stop working until they call the routes at the root /. Legacy authorization is also disabled by default, including on migrated installs. Check each client application before migrating.
Irreversible database migration. Jellyfin 12 stays on SQLite but restructures its database: playlists and collections become relational (new LinkedChildren table) and cleanup routines run automatically on first startup. These changes prevent any rollback without a full restore. Back up the entire config/ folder — especially data/jellyfin.db — before pulling the updated container image. Rolling back means restoring that backup together with the previous version's Docker image. After the upgrade, run a full library scan: it is required to restore alternate versions of your media.
10.11 plugin incompatibility. The server now targets .NET 10: plugins must be rebuilt for Jellyfin 12. The release notes recommend removing plugins installed from a repository (anything not built-in) before migrating, then reinstalling them from the official stable repository once compatible versions are published.
Direct migration supported. Upgrades from 10.10.7 and 10.11.x to 12 are supported — no need to reinstall from scratch or go through an intermediate version; below 10.10.7, upgrade to 10.10.7 first. Then pull the new Docker image, after backing up.
Scroll the table
| Criterion | Jellyfin | Plex |
|---|---|---|
| License / cost | Free (GPL), 100% free | Freemium, paid Plex Pass for key features |
| Hardware transcoding | Included and free | Reserved for Plex Pass subscribers |
| Remote access | Direct via your reverse proxy | Direct or via the Plex Relay (bandwidth-limited); remote video playback requires Plex Pass or Remote Watch Pass |
| Privacy | No telemetry, everything stays on your server | Centralized Plex account and metadata |
| Client quality (TV, mobile) | Good, steadily improving | Very polished and stable |
| DLNA support | Through the official DLNA plugin (host networking required in Docker) | Present, less configurable |
| Plugins and extensions | Rich community catalog, actively evolving | More closed, built-in features |
| Mobile apps | Free (Android, iOS) | Free, Plex Pass required for downloads |
| Community | Very active, open source, forums and Matrix | Active, official forum, less open |
| Chromecast | Supported via mobile app | Natively supported |
| Initial setup | A few manual settings | Very guided wizard |
Troubleshooting: The Most Common Errors
Transcoding that fails silently. The player shows a generic error or the video won't start. First check: in Dashboard → Logs, filter on Transcod. If the log reports FFmpeg as missing or a permission error on the transcoding folder, the container can't find the binary or can't write to that folder (/cache/transcodes by default). Verify the volume is mounted with write permissions and that the Docker image is jellyfin/jellyfin (not an unmaintained fork).
Hardware acceleration failure. On a VPS without a dedicated GPU, hardware acceleration (VAAPI, QSV, NVENC) is not available: transcoding fails if you enabled a hardware option the server cannot honor. Under Dashboard → Playback → Transcoding, disable hardware acceleration to fall back to software transcoding, or force Direct Play. On a machine with a GPU, the container needs access to it: map the render device (devices: - /dev/dri/renderD128:/dev/dri/renderD128, with group_add set to the host's render group) for VAAPI or QSV, or install the NVIDIA driver and the NVIDIA Container Toolkit on the host for NVENC.
Remote access blocked behind a reverse proxy (path prefix). If you serve Jellyfin under a subpath like your-domain.com/media/, static resources and the API won't load correctly. A dedicated subdomain (media.your-domain.com) avoids these pitfalls; the documentation also notes that a Base URL breaks the DLNA plugin, HDHomeRun, Sonarr and Radarr, among others. If a subpath is unavoidable, fill in the Base URL field under Dashboard → Networking (stored in network.xml, restart required) and ensure your reverse proxy forwards the full path without rewriting it.
Metadata sync that loops. The library scan runs indefinitely without progressing. Common causes: insufficient permissions on the media folder (Jellyfin must be able to read the files), filenames not conforming to the Title (Year).mkv standard, or connectivity issues to remote metadata agents. Check the scheduled task logs in Dashboard → Scheduled Tasks to identify which step is blocking.
To avoid saturating your VPS CPU, standardize your library in H.264/AAC inside an MP4 or MKV container. A simple ffmpeg remux script (no re-encoding, -c copy flag) repackages your files in a few seconds and relieves the server for good, especially on a VPS without a GPU. This operation doesn't touch video quality: it only changes the wrapper, not the content.