root@labs:~$ cat migrating-from-plex-to-jellyfin.md

Migrating from Plex to Jellyfin

Already running Plex and thinking about switching to Jellyfin? The good news: you don’t have to re-rip anything or “migrate” your media files at all — both point at the same folder on the same NAS. What actually needs migrating is your watch history: what’s been watched, what’s in progress, and your ratings. This guide covers that specifically, using a real, actively maintained tool built for exactly this.

Written for JellyPlex-Watched (luigi311/JellyPlex-Watched on GitHub), running via Container Manager on DSM 7.2. Check the project’s own repo for the current compose file before you start — sync tools like this one get updated often, and we’ll revise this guide if the setup steps change.

Step 1 — Install Jellyfin first, pointed at the same media folder

If you haven’t already, follow the Jellyfin install guide and map it to the exact same /volume1/media path Plex is already using. Let Jellyfin finish its own library scan before moving on — the sync tool matches items by filename and metadata provider ID, so both servers need to actually know about the same files first.

Step 2 — Get a Plex token

Open Plex Web, play any item, click the menu → Get InfoView XML. The URL that opens contains X-Plex-Token= followed by your token — copy everything after the equals sign. This is Plex’s own documented way to find it; no third-party tool required.

Step 3 — Get a Jellyfin API key

In Jellyfin, go to Dashboard → API Keys → +, give it a name like jellyplex-sync, and copy the generated key.

Step 4 — Set up JellyPlex-Watched

Make a docker/jellyplex-watched folder, and inside it a .env file:

PLEX_BASEURL=http://YOUR_NAS_IP:32400
PLEX_TOKEN=your-plex-token-from-step-2
JELLYFIN_BASEURL=http://YOUR_NAS_IP:8096
JELLYFIN_TOKEN=your-jellyfin-api-key-from-step-3
SYNC_INTERVAL=60
DRYRUN=True

Then in Container Manager → Project → Create, point it at that folder with this compose file:

services:
  jellyplex-watched:
    image: luigi311/jellyplex-watched:latest
    container_name: jellyplex-watched
    env_file: .env
    restart: unless-stopped
DRYRUN=True makes the tool log what it *would* sync without actually changing anything in Jellyfin. Deploy it this way first and check the container logs — confirm it’s matching your actual titles correctly before you let it write anything.

Step 5 — Go live

Once the dry run logs look right — real titles, sensible matches, no wall of “no match found” errors — edit the .env file to DRYRUN=False and redeploy the project. With SYNC_INTERVAL=60 it’ll re-sync every 60 minutes, which is enough to keep both servers agreeing on watch state during however long you run them side by side.

This tool matches by filename and metadata provider ID, not perfectly. Odd file naming, obscure titles, or content missing a proper IMDb/TMDb match can slip through. Spot-check a handful of recently watched titles in Jellyfin after the first real sync instead of assuming everything transferred.

Step 6 — Decide how long to run both

There’s no rush to cut Plex off. Keep both servers running against the same media folder, with the sync tool bridging watch state between them, for as long as it takes everyone in the household to actually switch over. When you’re ready, stop the Plex container (the media files themselves are untouched either way) and remove the sync tool — its job is done once there’s nothing left to keep in sync.

That’s a full migration without touching a single media file or losing a watch history you’ve built up over years. The only thing that actually moved was the metadata.

Leave a comment

Your email address will not be published. Required fields are marked *