Migrate Jellyfin by declaring its current behavior first, protecting persistent state, then adding services through reversible, tested stages.
This procedure is for a working Docker container that has outgrown an undocumented command or all-in-one layout. The target is not maximum container count; it is a reproducible Jellyfin service with explicit mounts, networks, devices, health signals, backup scope, and rollback. Keep media storage independent from application state, preserve the old instance until acceptance tests pass, and add only dependencies the household can operate.
Define What Resilience Must Cover
Choose the failures the new stack should handle: a Jellyfin process crash, a bad image update, lost configuration storage, an unavailable proxy, a host restart, or complete host loss. Each requires a different control. A restart policy helps after a process exit; it does not restore a deleted volume or repair an unreachable media mount.
Set measurable recovery targets for configuration, watch state, and service availability. Decide how much downtime and data loss are acceptable, who receives an alert, and which parts can be rebuilt. This scope prevents a small home migration from collecting databases, proxies, dashboards, and automation that do not reduce an identified risk.
Inventory the Running Container
Record the exact image reference, command, environment variables, published ports, networks, restart policy, user and group IDs, device mappings, DNS settings, labels, configuration mount, cache mount, media mounts, and secrets. Also capture ownership and permissions on every host path. A screenshot of a container UI is not a complete deployment record.
Translate that inventory into a Compose definition without changing behavior. The flag-by-flag method in this Docker run-to-Compose migration guide is valuable because it treats the first milestone as reproducibility, not feature expansion. Pin the currently running image digest or version for the initial cutover.
Separate Persistent State, Cache, and Media
Map Jellyfin configuration and database state to a clearly named persistent path. Put disposable cache and transcode segments on a separate path so they are not mistaken for critical backup data. Mount owned media independently and read-only where the workflow permits; a resilient application layer should not blur the protection boundary of a large media library.
Stop or quiesce Jellyfin before the first consistent state copy unless the backup method guarantees application consistency. Record permissions, checksums or file counts, backup time, and restore location. Never assume the container image contains user data: the deployment definition, secrets, persistent state, and media references are separate recovery inputs.
Prove Restore Before Changing the Network
Create a temporary restore target, copy the protected application state into it, and start the pinned Jellyfin service on an alternate port with media mounted read-only. Verify users, libraries, watch history, metadata, plugins, and representative playback. Destroy the temporary instance and repeat from the written procedure if any step depended on memory.
A practical Compose backup must preserve the deployment file, environment inputs, volumes, and any application-consistent database export. This Compose backup and upgrade guide explains why copying only an image or live database files is not a complete recovery path.
Cut Over to the Declarative Jellyfin Service
Choose a maintenance window, stop the old container, take the final consistent state backup, and prevent the old instance from automatically restarting. Start the equivalent Compose service with the same persistent paths and device access. Keep the public route unchanged only after local health and playback checks succeed.
Validate container health, logs, library visibility, hardware-device access, direct play, one representative transcode, subtitle handling, and a restart. If the service cannot see a device or mount, stop and restore the old container rather than editing multiple layers under pressure. The rollback is the previous pinned image plus the pre-cutover state and original run parameters.
Add Neighboring Services One Boundary at a Time
Introduce a reverse proxy only when remote ingress needs a separately managed route. Add monitoring when there is a defined health signal and someone will act on it. Add an alert channel when restart loops, storage loss, or backup failures must be noticed. Each service needs an owner, persistent-state decision, network scope, update method, and failure effect.
The architectural reason for these boundaries is covered separately in ZimaSpace's explanation of why Jellyfin deployments use service stacks. During migration, apply that model conservatively: group components that must recover together and avoid making playback depend on optional dashboards or automation.
Make Health, Updates, and Backups Observable
Define health at the user path, not merely as a running process. Check that Jellyfin answers locally, the media mount is present, the public route reaches the intended service when enabled, and one known file can be read. Route failed checks to a notification channel the operator already uses, with enough context to distinguish an application failure from storage or network loss.
Version the Compose definition, keep secrets out of the repository, and review image changes before deployment. Automate backups only after a manual restore works. The workflow in this Jellyfin health-check and monitoring guide illustrates how declarations, checks, alerts, and backups connect; preserve an approval and rollback point for updates that can change stored state.
Run Failure Drills Before Retiring the Old Path
Restart the host, stop Jellyfin unexpectedly, make the proxy unavailable, disconnect a test media path, and restore application state into a clean temporary location. Confirm the expected alert, recovery order, and user-visible behavior for each drill. Do not simulate destructive storage loss against the only media copy.
Record recovery time and any manual commands. A container that restarts quickly but returns with an empty library has failed the service test. A backup that exists but cannot be restored inside the target window has failed the recovery test. Fix those boundaries before adding more services.
Close the Migration With a Stable Operating Contract
Retire the original container only after the new Jellyfin service survives normal household use, a planned update, a host reboot, and a clean restore rehearsal. Archive the old parameters, final pre-cutover backup, current Compose definition, secrets recovery method, mount map, and rollback steps according to the chosen retention policy.
Stop expanding when the stack is reproducible, monitored, recoverable, and understandable by its operator. Add another node or dependency only when a measured capacity, trust, or failure-domain requirement demands it. Resilience comes from known state and practiced recovery, not from the number of containers in the diagram.
NAS & Server Setup
More to Read

How AI-Like Analysis and Automation Change Jellyfin Storage and Compute Needs
Automation and adjacent AI analysis add scans, derived data, CPU/GPU work, cache, scratch space, and background scheduling beyond ordinary Jellyfin playback.

How to Integrate Jellyfin Into a Small Apartment or Rental Network
Build a rental-friendly Jellyfin network around stable local addressing, minimal wiring, quiet hardware, CGNAT-aware remote access, and reversible changes.

How Many Users and Background Jobs Should One Jellyfin Host Support?
Treat Jellyfin users and background jobs as one shared workload budget; capacity ends when playback latency, queues, or resource pressure becomes repeatable.

