Back Up and Restore Jellyfin and the Media Automation Stack
This runbook separates the small, rebuild-critical state of a media server from the much larger media library, then shows how to prove that state can be recovered without touching production.
Rights and lawful use: Back up Jellyfin, Plex, Arr, SABnzbd, Prowlarr, and Tdarr configuration only for systems and media workflows you are authorized to operate. A recovery archive should preserve legitimate application state and restore notes, not become a way to retain, move, or share content without permission.
Quick Answer
Back up application databases, configuration, secrets, Compose files, mount definitions, encryption keys, and version records separately from bulk media. Use each application's supported backup method or stop it before copying live SQLite state, encrypt the archive, and keep at least one failure-independent copy. A completed archive is not proof of recovery: restore it on an isolated target with the recorded application versions before an upgrade or outage makes the test urgent.
What This Runbook Is Designed to Recover
The reader intent is specific: rebuild Jellyfin or Plex and the surrounding automation stack without recreating users, watch state, quality profiles, root folders, indexer links, download categories, transcode flows, and host wiring from memory. It is not a promise that every media file will be backed up or that one generic script fits every package, container image, database, and operating system.
| Recovery tier | Examples | Backup method | Recovery priority |
|---|---|---|---|
| Tier 0: keys and access | Encryption keys, backup credentials, license information, administrator recovery codes | Encrypted, access-controlled copy with separate key custody | Required before any encrypted archive or protected service can be recovered |
| Tier 1: host definition | Compose files, environment files, systemd units, mounts, user/group IDs, firewall, DNS, proxy, cron, scripts | Versioned encrypted archive plus written inventory | Restore before application state |
| Tier 2: application state | Databases, settings, users, profiles, integrations, queues, history, metadata | Built-in backup or a cold, consistent filesystem copy | Restore after paths and permissions exist |
| Tier 3: derived data | Thumbnails, transcode cache, trickplay, downloaded metadata, temporary files | Include only when its rebuild cost justifies the space | Optional or later |
| Tier 4: media | Authorized movies, shows, music, photos, and home video | Independent storage policy based on replacement cost and legal obligations | Required for irreplaceable media; optional only when a lawful source can recreate it |
Set a recovery point objective (RPO) and recovery time objective (RTO) for each tier. For example, an operator might accept losing one day of queue history but not the encryption key needed to open every backup. Those are policy examples, not TechGeeks measurements. Record the actual acceptable age and restore deadline for your environment.
Inventory the Authoritative State
| Component | State to protect | Consistency rule | Restore note |
|---|---|---|---|
| Jellyfin | Data and config directories; database; selected metadata, subtitles, and trickplay; package or image version | Use the 10.11 built-in backup during low activity with no scan running, or stop Jellyfin for a full manual copy | Record the exact version and installation type; Jellyfin has no in-place downgrade mechanism |
| Plex | Main Plex Media Server data directory, Preferences.xml on Linux/NAS, platform-specific settings, scheduled database backups | Stop Plex before a whole-directory copy or database replacement | Restore ownership and keep archive files outside the live Plex data directory |
| Sonarr, Radarr, Prowlarr | Config directory, database, built-in backups, profiles, custom formats, tags, root folders, integrations | Prefer built-in backups; stop the service before a raw config-directory copy | Validate root paths, download client, indexer/app sync, and permissions |
| Seerr | settings.json plus SQLite or PostgreSQL data | Stop Seerr before copying SQLite unless using a supported snapshot or SQLite backup; use pg_dump for PostgreSQL | Validate users, requests, integrations, and notification settings |
| SABnzbd | sabnzbd.ini, categories, server definitions, folder paths, queue/history policy | Stop before copying active configuration, or use its supported backup workflow when available | Do not expose provider credentials or API keys |
| Tdarr | Server and node configuration, flows/plugins, schedules, scripts, cache path, library definitions | Stop server and nodes before a raw state copy | Validate one synthetic, authorized transcode and cache cleanup before resuming the queue |
| Host and network | Compose, .env, service units, UID/GID map, fstab or mount units, DNS, TLS, proxy, firewall, cron, GPU mapping | Archive after each material change | Restore mounts and identities before applications |
Configuration backups are credential archives. They can contain API keys, password hashes, provider credentials, server identities, private hostnames, and user data. Do not place them in a public code repository or an unencrypted shared folder. Keep the decryption identity outside the protected host and test that an authorized recovery operator can retrieve it.
Jellyfin 10.11 Backup and Restore Boundaries
Jellyfin 10.11 added built-in live backup and restore. The current administration guide says the database is always included; metadata, subtitles, and trickplay are selectable. The server checks for at least 5 GB free in the backup folder, but those optional data sets can require more. The guide recommends creating the online backup during low activity while no library scan is active.
- Open Dashboard > Backups and create a backup.
- Include the database. Select metadata, subtitles, and trickplay only when they are part of the required recovery point and the destination has enough space.
- Copy the resulting ZIP to encrypted, failure-independent storage. A backup inside the Jellyfin data directory is still lost with that directory.
- Record the Jellyfin server version, image digest or package version, install type, config/data paths, archive checksum, and backup time.
- For a full blank-host recovery drill, also take a manual copy of all data and configuration while Jellyfin is stopped. The 10.11 release notes describe built-in restore as same-system recovery, not a cross-operating-system or third-party-container migration tool.
Jellyfin applies database migrations when a newer version starts and does not provide an in-place downgrade. If an upgrade must be reversed, stop the newer server, restore a backup created before the migration, and start the recorded older version. Do not point an older binary at data already migrated by a newer one.
# Example only - not performed for this article.
# Manual package backup on Debian/Ubuntu; requires age.
set -euo pipefail
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
RECIPIENTS=/root/media-backup-recipients.txt
command -v age >/dev/null
sudo mountpoint -q /mnt/backup
sudo test -r "$RECIPIENTS"
sudo install -d -m 0700 "/mnt/backup/jellyfin/$STAMP"
sudo systemctl stop jellyfin
trap 'sudo systemctl start jellyfin' EXIT
sudo tar --acls --xattrs --numeric-owner \
-C / -cpf - var/lib/jellyfin etc/jellyfin \
| sudo age -R "$RECIPIENTS" \
-o "/mnt/backup/jellyfin/$STAMP/jellyfin-state.tar.age"
sudo systemctl start jellyfin
trap - EXIT
sudo sha256sum "/mnt/backup/jellyfin/$STAMP/jellyfin-state.tar.age" \
| sudo tee "/mnt/backup/jellyfin/$STAMP/SHA256SUMS"
Expected result (example, not observed): Jellyfin returns to an active state, the encrypted archive and checksum file exist on the backup target, and sha256sum -c SHA256SUMS reports jellyfin-state.tar.age: OK. That result proves only file integrity since hashing; it does not prove the archive will decrypt, restore, or survive loss of the protected host.
Plex and Arr Recovery Notes
Plex documents a backup of the main Plex Media Server data directory, with optional exclusion of the Cache directory on Windows and Linux. Keep the archive outside the live Plex data directory. The scheduled database restore procedure requires stopping Plex, moving the current database and any -shm or -wal files aside, copying the dated backup into place, restoring the normal filenames, and verifying that the Plex service account can read and write them.
For Sonarr, Radarr, and Prowlarr, keep the applications' own backup archives and an encrypted copy of the persistent /config directories. The built-in archive is the first choice for application-level recovery. A stopped filesystem copy supports whole-host recovery and preserves surrounding files that the built-in export might not own. Do not copy a live SQLite database with a plain recursive copy and assume consistency.
Controlled Encrypted Backup Example
The following root-owned Bash example preserves the intent of the original NAS backup script while making consistency and secret handling explicit. It assumes Docker Compose, host paths under /srv/appdata, an installed age client, a mounted backup target, and a recipient file containing only public age recipients. APPDATA_ROOT must be the exact common parent of the listed persistent bind-mount sources; use app-specific paths if your Compose file has no such parent. Change every path and service name, run the installed script manually with sudo, and test an isolated restore before scheduling it.
# Example only - not performed for this article.
sudo install -d -m 0750 /usr/local/sbin /var/log/media-backups
sudo tee /usr/local/sbin/media-stack-backup.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
exec 9>/run/lock/media-stack-backup.lock
flock -n 9 || { echo 'backup already running' >&2; exit 1; }
STACK_DIR="/opt/media-stack"
COMPOSE_FILE="$STACK_DIR/compose.yml"
APPDATA_ROOT="/srv/appdata"
BACKUP_MOUNT="/mnt/backup"
BACKUP_ROOT="$BACKUP_MOUNT/media-stack"
RECIPIENTS="/root/media-backup-recipients.txt"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
OUT="$BACKUP_ROOT/$STAMP"
services=(jellyfin plex sonarr radarr prowlarr sabnzbd tdarr)
appdirs=(jellyfin plex sonarr radarr prowlarr sabnzbd tdarr)
test -r "$COMPOSE_FILE"
test -r "$RECIPIENTS"
test -d "$APPDATA_ROOT"
mountpoint -q "$BACKUP_MOUNT"
for appdir in "${appdirs[@]}"; do
test -d "$APPDATA_ROOT/$appdir"
done
install -d -m 0700 "$OUT"
cd "$STACK_DIR"
docker compose -f "$COMPOSE_FILE" config --services >"$OUT/services.txt"
for service in "${services[@]}"; do
grep -Fxq "$service" "$OUT/services.txt" || {
echo "Compose service not found: $service" >&2
exit 1
}
done
docker compose -f "$COMPOSE_FILE" images >"$OUT/images.txt"
mapfile -t running_services < <(
docker compose -f "$COMPOSE_FILE" ps --services --filter status=running
)
restart_running() {
if ((${#running_services[@]})); then
docker compose -f "$COMPOSE_FILE" up -d "${running_services[@]}"
fi
}
trap restart_running EXIT
docker compose -f "$COMPOSE_FILE" stop "${services[@]}"
tar --acls --xattrs --numeric-owner \
-C "$APPDATA_ROOT" -cpf - "${appdirs[@]}" \
| age -R "$RECIPIENTS" -o "$OUT/app-state.tar.age"
stackfiles=(compose.yml)
[[ -f "$STACK_DIR/.env" ]] && stackfiles+=(.env)
tar --acls --xattrs --numeric-owner \
-C "$STACK_DIR" -cpf - "${stackfiles[@]}" \
| age -R "$RECIPIENTS" -o "$OUT/stack-definition.tar.age"
(cd "$OUT" && sha256sum *.age >SHA256SUMS)
trap - EXIT
restart_running
EOF
sudo chmod 0750 /usr/local/sbin/media-stack-backup.sh
The script intentionally exits when the backup target is not a mount point, a listed service or app-data directory is absent, or another copy holds the lock. Those preflight checks happen before the stop. It records which services were running, installs an exit trap, stops the listed services, and restarts only the previously running set after success or an archive failure. The common cold copy creates downtime and may not suit every stack. Jellyfin's supported online backup and application-specific built-in backups can reduce downtime, but they still need off-host copying, checksums, retention, encryption, and a restore drill.
Do not add this to cron until a manual sudo /usr/local/sbin/media-stack-backup.sh run and isolated restore pass. Install the schedule with sudo crontab -e so its runtime privileges match the root-owned paths and Docker access used by the script. A root crontab entry can look like the following:
# Example only - not installed or run for this article.
# Run daily at 03:15 local system time.
15 3 * * * /usr/local/sbin/media-stack-backup.sh >>/var/log/media-backups/cron.log 2>&1
Restore in Dependency Order
- Isolate the recovery host from production DNS, proxy routes, webhooks, download providers, and automatic clients.
- Install the base operating system and the recorded container runtime or packages. Do not start the application stack yet.
- Restore users, groups, storage mounts, encryption keys, and stable paths. Verify read/write access with a non-production test directory.
- Restore Compose files, environment files, systemd units, firewall rules, and local certificates. Replace production endpoints with isolated test values where necessary.
- Verify checksums, decrypt archives into a staging path, inspect ownership, and copy state into the final directories while services remain stopped.
- Start databases and infrastructure first, then Prowlarr, Sonarr/Radarr, SABnzbd, Jellyfin or Plex, Seerr, and Tdarr. The exact order can differ when your dependency graph differs.
- Validate users, paths, permissions, integrations, and one synthetic authorized workflow. Keep full automation paused.
- Record the achieved recovery point and time, then either destroy the test target or approve a controlled production cutover.
A rebuild works best when dependencies return in order instead of all at once.
1. HostOS + Users
Rebuild the host, users, media group, packages, Docker, and GPU driver prerequisites.
2. StorageMounts + Paths
Restore /data paths, NAS mounts, marker files, cache directories, and permissions.
3. ConfigCompose + App DBs
Restore compose files, environment, app config folders, databases, and service units.
4. Core AppsProwlarr / SAB / Arr
Start source, download, and import apps before Plex/Tdarr broad processing.
5. PlexLibraries
Start Plex after media paths are correct so it does not see empty or wrong folders.
6. TdarrAutomation Last
Resume Tdarr only after imports, playback, API calls, and backups are verified.
Verify the Archive Before Restoring
# Example only - not performed for this article.
# Replace the example set, then run the whole block on an isolated host.
sudo bash <<'EOF'
set -Eeuo pipefail
BACKUP_SET=/mnt/backup/media-stack/20260824T000000Z
cd "$BACKUP_SET"
sha256sum -c SHA256SUMS
install -d -m 0700 /srv/restore-stage/app-state
age -d -i /root/media-backup-identity.txt app-state.tar.age \
| tar --acls --xattrs --numeric-owner -xpf - \
-C /srv/restore-stage/app-state
find /srv/restore-stage/app-state -maxdepth 2 \
-printf '%M %u:%g %p\n' | sort
EOF
Expected output (example, not observed): each encrypted archive reports OK; extraction exits with status 0; and the ownership listing matches the recorded UID/GID plan. This does not prove database consistency. Continue with application-level checks on an isolated host.
Validation and Acceptance Tests
| Check | Acceptance result | Evidence to retain |
|---|---|---|
| Archive integrity | All recorded checksums pass from the independent copy | Command, UTC time, exit code, checksum file, storage location |
| Application startup | No database migration, lock, ownership, or missing-secret error remains | Version record and redacted startup log |
| Identity | Expected synthetic users and permissions exist; no production user is contacted | Redacted UI or API inventory |
| Storage paths | Test account can read the intended path and cannot write outside its scope | Read/write and safe negative permission result |
| Integrations | Prowlarr-to-Arr and Arr-to-download-client tests use isolated endpoints and succeed | Redacted connection-test result |
| Media service | One authorized synthetic item is indexed and direct-played; watch state can be updated | Client/server version and redacted playback evidence |
| Tdarr | One synthetic file completes the intended flow and the output hash is recorded | Flow version, job record, input/output properties |
| Recovery objectives | Measured backup age and restore duration meet the documented RPO and RTO | Start/end times and any gaps |
Troubleshooting Failed Restores
- Database locked or corrupt: stop the application. Restore a supported built-in backup or a cold copy; do not keep retrying writes against a questionable SQLite file.
- Wrong owner or permissions: compare the fresh install's service UID/GID and directory modes with the archive. Correct the staged copy before starting the service.
- Missing API keys or passwords: keep automation disabled, retrieve the protected secret bundle, and rotate any credential that may have been exposed. Do not copy secrets into screenshots or tickets.
- Paths are wrong after migration: mount storage at the recorded path or use the application's supported path-remapping procedure. Confirm both container-side and host-side paths.
- Newer version rejects old state: install the recorded source version first, prove the restore, and follow the vendor-supported upgrade path. Do not repeatedly launch different versions against the same only copy.
- Jellyfin rollback fails: stop the newer server and restore the complete pre-upgrade data/config backup before starting the older version. A package downgrade without data rollback is not a Jellyfin rollback.
- Plex opens but libraries fail: verify the Plex data directory, database files, service ownership, server identity settings, and storage paths. A working UI does not prove playback.
- Apps start before mounts: stop them immediately, restore the correct mount dependency, and inspect for accidental writes into empty local mount points before resuming.
Rollback and Production Recovery
An isolated drill should never require a production rollback because it does not replace production state. If a real cutover fails, stop writes on both environments, preserve logs and the failed target, and return clients to the known-good endpoint. Do not synchronize the failed target back into the source. If the failure involves database migration, encryption keys, or uncertain filesystem integrity, work from copies and retain the original media and application state unchanged.
Recovery is complete only after the application-level tests pass, scheduled backups are running again, monitoring can report failures, the backup copy remains independent of the recovered host, and any temporarily exposed tokens have been rotated.
Security, Privacy, Legal, and Recovery Boundaries
- Security: encrypt backups containing secrets, use a dedicated backup identity with least privilege, restrict management interfaces to a trusted network or private VPN, and keep decryption keys outside the protected host.
- Privacy: databases, metadata, filenames, watch history, request records, hostnames, addresses, and logs can identify people and interests. Minimize collection, define retention, and redact reviewed evidence at original resolution.
- Legal: retain and restore only data and media you are authorized to use. Automation, Usenet, torrent protocols, remote access, and backup software do not change copyright, provider, privacy, employment, or deletion obligations.
- Recovery: parity, RAIDZ, snapshots, replication, and an archive on the same host are not independent backup. Preserve version records, key custody, off-host copies, and a tested blank-host path.
What the Available Evidence Does Not Prove
This revision is documentation-backed. TechGeeks did not run the example script, create the pictured archives, restore a named Jellyfin, Plex, Arr, SABnzbd, Seerr, or Tdarr environment, or measure RPO, RTO, archive size, or duration. A checksum proves that a file matches the recorded checksum; it does not prove the database is consistent. A successful disposable restore would not prove offsite durability, media integrity, secret safety, DNS readiness, or compatibility across every version and container image.
Evidence-Capture Checklist Before Publication
- Create
artifacts/labs/backup-disaster-recovery-plex-sonarr-radarr-tdarr/YYYY-MM-DD/with a stable run ID, environment record, UTC times, versions, image digests, config hashes, topology, and synthetic test media details. - Capture a baseline, backup point, exact commands, working directories, exit codes, encrypted archive checksums, sizes, durations, and retention state.
- Restore disposable Jellyfin/Plex and selected Arr state without production DNS, credentials, providers, notifications, or library data.
- Record one safe negative test for wrong ownership or a missing secret, the visible failure, the correction, and a new artifact ID.
- Verify users, paths, profiles, integrations, one authorized playback, one controlled processing job, alerts, and a representative restore from the independent copy.
- Record measured RPO and RTO. Do not turn planned table cells into numbers until reviewed evidence exists.
- Capture only genuine screenshots from the performed run. Redact names, emails, titles, history, hostnames, addresses, tokens, paths, device IDs, and notifications; remove metadata and complete second-person review.
- Keep raw artifacts private and reference only reviewed redacted derivatives in the publishing bundle.
Related TechGeeks and Series Navigation
This recovery installment turns the series' individual media, automation, proxy, and processing configurations into a rebuild sequence. Use the service guides to identify each persistent config path, then use monitoring to prove mounts, imports, playback, GPU access, and automation after restoration.
- Plex Homelab Architecture: Storage, GPU Transcoding, and Library Design
- Media Server Storage Design: NAS, CIFS/NFS Mounts, Permissions, and Local Cache
- Prowlarr Setup Guide: Clean Indexer Management for Sonarr and Radarr
- Sonarr Homelab Setup Guide
- Radarr Homelab Setup Guide
- SABnzbd Performance Tuning for a Homelab Media Server
- Monitoring and Health Checks for a Plex and Arr Homelab
- The Complete Plex, Arr, and Tdarr Homelab Media Automation Series
- Docker Volume and Database Backup Guide
References
- Jellyfin: Backup and Restore
- Jellyfin 10.11.0 release notes
- Plex: Backing Up Plex Media Server Data
- Plex: Restore a Database Backed Up via Scheduled Tasks
- Emby: How to Back Up Manually
- Seerr: Backups
- Servarr Wiki
- NIST SP 800-184: Guide for Cybersecurity Event Recovery
Final Thought
Do not make the first full restore attempt during an outage. Inventory the small state that actually rebuilds the stack, protect its secrets, and prove one isolated recovery while the production server is still healthy.
Need help applying this?
Bring TechGeeks into the real environment.
If you are working through this on a live network, WordPress site, Linux server, AI workflow, or PisoWiFi deployment, send the context and we can help turn it into a practical plan.



5 thoughts on “Back Up and Restore Jellyfin and the Media Automation Stack”