Validate the new Jellyfin server with copied state and disposable media first; production data moves only after playback, recovery, and rollback pass.
Treat migration as one controlled path with the old server remaining authoritative. Restore a versioned checkpoint onto an isolated target, reproduce its logical mounts and runtime identity, then exercise the clients, codecs, subtitles, remote route, scanners, and restart behavior that actually matter. A dashboard that opens is only the first gate; the weakest failed dependency decides the result.
Freeze the Production Baseline and Rollback Line
Record the source Jellyfin version, installation method, runtime UID/GID or service account, configuration and cache locations, logical media paths, hardware device mappings, reverse-proxy address, certificates, users, library counts, scheduled jobs, plugins, and one known-good playback example for every critical path.
Define failure before touching the target: database migration error, missing library, wrong ownership, broken login, absent hardware acceleration, failed critical client, or rollback longer than the outage budget. This converts migration from a vague confidence check into observable gates.
Create a coherent checkpoint and keep the source unchanged after the test baseline. A recent migration failure report illustrates why application-version pairing belongs in the baseline: recovery can fail at the database migration boundary even when the files exist.Build a Copy-Only Staging Path
Install the target at the same Jellyfin version as the checkpoint, then restore into isolated storage. Copy representative media or mount a small test subset read-only. Do not rename, delete, or reorganize production files to make the candidate work; every destructive change removes rollback evidence.
Give the target a temporary hostname, address, and client endpoint. Prevent scheduled jobs, webhooks, downloaders, or automation from treating both instances as active. Two servers may read the same immutable sample, but they must not write the same database, cache, metadata tree, or ingest location.
If the platform itself is changing, reproduce one boundary at a time: container path, service identity, storage protocol, network route, then accelerator access. The recoverable container deployment provides a deeper path for declaring mounts and persistent state.Clear Identity, Path, and Version Gates
Start the candidate and inspect logs before opening the dashboard. Confirm that it loaded the restored server identity rather than launching first-run setup, that every expected media path is mounted, and that the runtime can read media and write only the intended state and cache paths.
Restart the entire target, not only the application. Verify dependency order, storage mounts, DNS, proxy routing, certificates, scheduled tasks, plugins, and GPU device access after a cold start. A successful interactive start can hide a boot-order or permission failure.
Stop on any database migration warning, empty library caused by a missing mount, owner mismatch, path rewrite, or software-transcode fallback that was supposed to be accelerated. Use the identity and state checklist to compare the restored instance against its known-good source.
Run a Representative Workload Matrix
Test outcomes, not menus. Use the same file, client, subtitle track, output resolution, and network path as the baseline. Read the Jellyfin dashboard and transcode logs during each run, and record start time, buffering, dropped frames, CPU/GPU use, and whether the mode was Direct Play, remux, or transcode.
| Path | Representative test | Pass condition |
|---|---|---|
| Local direct | Known compatible client and file | Direct Play, stable seek, no new errors |
| Subtitle | Common text track and hardest image/styled track | Correct rendering and real-time playback |
| HDR/transcode | Worst required conversion | Expected accelerator, speed above real time |
| Concurrency | Realistic simultaneous sessions | No saturation or starvation |
| Library | Incremental scan and metadata read | No path duplication or lost custom data |
| Remote | External client through normal route | Authentication, certificate, bitrate, and playback pass |
A passed easy file cannot substitute for the hardest required row. If one critical client or subtitle path fails, either fix that dependency and rerun the matrix or explicitly remove it from the production requirement before cutover.
Prove Recovery, Then Cut Over Once
Take a fresh target checkpoint, destroy only the disposable target state, and restore it cleanly. Repeat login, library, playback, restart, and scheduled-task checks. The independent pre-upgrade restore guide reinforces the practical boundary: a backup earns trust by surviving a restore and restart.Schedule one cutover window. Pause source-side changes, take the final state checkpoint, sync the planned media delta, restore or update the target, then change the single client-facing endpoint. Run the blocking rows again before allowing normal writes or library maintenance.
Keep the old server powered down or isolated but intact for the observation window. Roll back by restoring the original endpoint, not by copying uncertain target state backward. Retire the source only after the new server passes normal load, a scheduled restart, a backup cycle, and the agreed recovery window.
NAS & Server Setup
More to Read

How to Separate Home Assistant App Data, Cache, and Backups
Keep authoritative app state persistent, prove cache is disposable before moving it, and store tested backups outside the Home Assistant failure domain.

How to Adapt a Home Assistant Setup for Remote and Local Users
Keep local Home Assistant control independent of the remote edge, then add secure remote access with predictable DNS, identity, and network-switching behavior.

How to Move Home Assistant From a Single Container to a Resilient Service Stack
Preserve working state first, then separate data, dependencies, health, resources, and recovery so one service failure does not take down Home Assistant.

