Tutorial

Migrate Plex to Jellyfin and Navidrome on a VPS

Self-hosting8 min read12 steps

In September 2026, the Shadowserver Foundation counted over 36,000 internet-exposed Plex instances still running versions older than 1.43.3 — the release that patches two critical vulnerabilities enabling arbitrary file writes and directory traversal. If you self-host Plex on a root VPS, the exposure window is real and documented. This guide shows you how to switch to Jellyfin (video) and Navidrome (music) without losing your libraries, metadata, or playlists, and why the cost of migrating is lower than the risk of staying.

Contents· Why leave Plex in 2026: 36,000 servers exposed1/9
  1. 01Why leave Plex in 2026: 36,000 servers exposed
  2. 02What Plex hides behind its cloud model
  3. 03Prerequisites before starting the migration
  4. 04Plex → Jellyfin migration: step-by-step
  5. 05Music migration: Plex → Navidrome
  6. 06Plex vs Jellyfin vs Navidrome comparison
  7. 07Nginx reverse proxy configuration for Jellyfin
  8. 08Troubleshooting: the 3 most common migration errors
  9. 09Conclusion

Why leave Plex in 2026: 36,000 servers exposed

On September 9, 2026, Shadowserver published its findings: over 36,000 Plex servers accessible from the internet had not applied the 1.43.3 update, available since May 19. Two CVEs are at the core of this advisory. CVE-2026-96656 (CVSS 7.2) lets an admin-level attacker write arbitrary files that are executed on startup — concretely, load a malicious plugin that drops a rootkit, with no signature or execute-bit check. CVE-2026-96651 (CWE-22, path traversal) allows any authenticated session to read any file accessible to the Plex system user, including the PlexOnlineToken. Combined, these two flaws cover a full escalation scenario: session theft, then code execution. Plex patched both in build 1.43.3.10861; build 1.43.3.10896 is the recommended target. The structural problem remains: Plex is proprietary software whose patch cycle depends on a single vendor, and 36,000 administrators had not applied a four-month-old fix.

What Plex hides behind its cloud model

Plex offers a polished interface, but its cloud model introduces hidden dependencies that become problematic in the event of a security patch or service discontinuation.

  • Remote access via Plex Relay: every stream goes through plex.tv servers, even if your VPS is the only one storing the files. Plex sees your playback metadata.
  • Plex Pass required for key features: offline sync, full multi-user access, Live TV, and remote access without port forwarding are reserved for paying subscribers.
  • Centralized authentication: your clients' logins go through Plex servers. A plex.tv outage locks you out of your own files.
  • Uncontrolled updates: Plex can deploy server-side changes (relay, authentication) without you being able to audit or block them.
  • Active telemetry by default: playback, searches, and viewing habits are sent to the vendor unless manually disabled in each client.

Prerequisites before starting the migration

This procedure targets a root VPS running Debian 12 or Ubuntu 22.04 LTS. You need SSH root or sudo access, Docker and Docker Compose installed (v2+), and a media directory accessible for reading. Take a VPS snapshot before starting — snapshots are reversible in one command and cost less than an hour of compute. Jellyfin replaces the video library; Navidrome replaces the music library. Both are open source, subscription-free, and exposed behind an Nginx reverse proxy.

Plex → Jellyfin migration: step-by-step

Here is the complete procedure for migrating your Plex library to Jellyfin without losing your data or watch history.

  1. Create a VPS snapshot

    First: snapshot create plex-before-migration from your provider's panel (or equivalent API). This step is your safety net; do not skip it.

  2. Stop Plex cleanly

    systemctl stop plexmediaserver
    systemctl disable plexmediaserver

    Wait for the shutdown confirmation before continuing. Plex must no longer be listening on port 32400.

  3. Install Jellyfin via Docker Compose

    # /opt/jellyfin/docker-compose.yml
    services:
      jellyfin:
        image: jellyfin/jellyfin:latest
        container_name: jellyfin
        restart: unless-stopped
        ports:
          - "8096:8096"
        volumes:
          - /opt/jellyfin/config:/config
          - /opt/jellyfin/cache:/cache
          - /data/media:/media:ro
        environment:
          - JELLYFIN_PublishedServerUrl=https://jellyfin.your-domain.com

    Replace /data/media with the exact path to your media files. The volume is mounted read-only (:ro) as a precaution.

  4. Start Jellyfin and run the setup wizard

    cd /opt/jellyfin && docker compose up -d

    Access http://VPS-IP:8096 for the web wizard. Create the admin account, then add your libraries pointing to /media/films, /media/series, etc.

  5. Scan libraries and verify metadata

    Jellyfin uses The Movie Database (TMDB) and TheTVDB by default. The first full scan takes between 5 and 30 minutes depending on library size. Verify that covers, synopses, and ratings display correctly. If a title is not automatically recognized, use the Identify function to force the match.

  6. Configure Jellyfin clients

    Official Jellyfin apps are available on Android, iOS, Apple TV, Android TV, Roku, and browsers. They are free and open source. Point each client to your server's internal or public URL — no third-party authentication required.

  7. Verify transcoding and subtitles

    In Dashboard > Playback, enable hardware transcoding if your VPS has a GPU (Intel Quick Sync, NVIDIA NVENC). Test an H.265 file from a mobile client to validate that software transcoding works as fallback. Also verify that SRT and ASS/SSA subtitles display correctly.

  8. Remove Plex

    apt remove plexmediaserver --purge
    rm -rf /var/lib/plexmediaserver

    Only remove the old installation after validating Jellyfin on at least one client for 48 hours.

Music migration: Plex → Navidrome

Navidrome is an open source music server compatible with the Subsonic API, giving it access to the entire client ecosystem (DSub, Symfonium, Ultrasonic, Airsonic, etc.). It reads ID3 v2.3 and v2.4 tags, FLAC, MP3, AAC, OGG, and Opus files. M3U playlists present in your music directory are imported automatically, provided paths are relative to the music root.

  1. Check folder structure

    Navidrome expects an Artist/Album/Track.mp3 hierarchy. If your files are already organized this way (which Plex also requires), no reorganization is needed. Check for special characters in folder names (/, :, ?).

  2. Audit ID3 tags

    find /data/music -name '*.mp3' | head -20 | xargs id3v2 -l

    Fields TPE1 (artist), TALB (album), and TIT2 (title) must be filled in. A file without these three tags will be imported but with no displayable metadata. The beets tool can fix missing tags in bulk via its MusicBrainz database.

  3. Deploy Navidrome via Docker Compose

    # /opt/navidrome/docker-compose.yml
    services:
      navidrome:
        image: deluan/navidrome:latest
        container_name: navidrome
        restart: unless-stopped
        ports:
          - "4533:4533"
        volumes:
          - /opt/navidrome/data:/data
          - /data/music:/music:ro
        environment:
          - ND_SCANSCHEDULE=1h
          - ND_LOGLEVEL=info
          - ND_BASEURL=

    Start with docker compose up -d then access http://IP:4533 to create the first account.

  4. Migrate Plex playlists

    Plex does not natively export playlists in M3U format from the interface. Use the plexapi Python tool to export: python3 -m plexapi.playlist export --token YOUR_TOKEN --output /data/music/playlists/. The generated M3U files are automatically imported by Navidrome if paths are relative to the /music volume.

Plex vs Jellyfin vs Navidrome comparison

The following table compares the essential criteria for choosing between Plex, Jellyfin, and Navidrome.

Scroll the table

CriterionPlexJellyfinNavidrome
LicenseProprietary (freemium)GPL-2.0 open sourceGPL-3.0 open source
Remote access without subscriptionNo (Plex Pass required)Yes, nativeYes, native
Third-party authentication requiredYes (plex.tv)NoNo
Video libraryYesYesNo (music only)
Music libraryYes (limited)PartialYes (specialized)
Hardware transcodingPlex Pass requiredNative (NVENC, QSV, VAAPI)N/A (audio only)
Official mobile clientsPaid on iOS/AndroidFree, open sourceThird-party Subsonic clients
Cloud dependencyStrong (relay, auth)NoneNone

Nginx reverse proxy configuration for Jellyfin

Jellyfin must be exposed via HTTPS. The configuration below assumes a Let's Encrypt certificate managed by Certbot or your CDN in Full (strict) mode. Never expose port 8096 directly — always go through Nginx.

# /etc/nginx/sites-available/jellyfin
server {
    listen 80;
    server_name jellyfin.your-domain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name jellyfin.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/jellyfin.your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jellyfin.your-domain.com/privkey.pem;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";

    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;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_buffering off;
        proxy_read_timeout 3600;
    }
}

Apply the same structure for Navidrome, replacing the port with 4533. Activate the config with nginx -t && systemctl reload nginx.

Troubleshooting: the 3 most common migration errors

Here are the three most common problems during a Plex/Jellyfin migration, with their direct solutions.

  • Library empty after first scan: verify that the Docker-mounted path exactly matches the directory containing your files. docker exec jellyfin ls /media should list your folders. If the volume is empty, fix the mapping in docker-compose.yml and restart the container.
  • Transcoding fails with ffmpeg error: Jellyfin ships its own version of ffmpeg. If the container has no GPU access, disable hardware transcoding in Dashboard > Playback and switch to software transcoding. On a VPS without a GPU, this is the normal configuration.
  • iOS/Android clients cannot find the server: make sure the firewall allows port 443 from outside (ufw allow 443), your DNS points to the VPS IP, and the TLS certificate is valid. Test with curl -I https://jellyfin.your-domain.com/health from your local machine.

Keep Plex disabled (not uninstalled) for the first 48 hours after migration. The VPS snapshot lets you roll back in under five minutes. Only delete Plex config files (/var/lib/plexmediaserver) after confirming all your clients work correctly on Jellyfin and Navidrome.

Conclusion

36,000 unpatched Plex servers are not a sign of individual negligence: they reveal a structural dependency on a proprietary vendor whose patch cycle is opaque. CVE-2026-96656 and CVE-2026-96651 show that this dependency has a measurable cost. Jellyfin and Navidrome give you back control: no third-party authentication, no cloud relay, no subscription for remote access. The migration takes under an hour with this guide; the library is intact on arrival. If you host your media on a root VPS, there is no longer any reason to wait.

Host Jellyfin on a root VPS today

ServOrbit offers Jellyfin preconfigured on a root VPS starting at 99 DH/month. One-click deployment, Nginx reverse proxy included, no subscription or third-party cloud.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab