Jellyfin Security: Treat Media as Untrusted Input
Security and lawful-use boundary: Do not play, upload, redistribute, or build proof-of-concept exploit media. Work only on systems and files you are authorized to inspect. If compromise, regulated data, employee monitoring, notification duties, or legal evidence handling may apply, preserve the original state and involve qualified incident-response and legal personnel before destructive cleanup.
Quick Answer
Jellyfin releases before 10.11.10 are affected by the two June 2026 subtitle and Matroska (MKV) advisories. Stop new untrusted imports, avoid playing suspect files, preserve logs and filesystem evidence, and upgrade to a supported current stable release after reviewing migration requirements. On October 2, 2026, the official latest release is 12.1; validate its major-version path on an isolated canary before production. Then make media mounts read-only where practical and retest subtitles, Direct Play, transcoding, identity, clients, and recovery. Patching closes documented paths; it does not prove the host was never compromised.
Evidence status: This is a documentation-backed response plan. The advisories and current release/backup documentation were checked on October 2, 2026. Shell examples received syntax checks only; no incident collection, media test, upgrade, screenshot, denied-write result, restore, or compromise-eradication finding is claimed.
What the June 2026 Advisories Establish
| Advisory | Affected | Fixed | Documented condition and impact |
|---|---|---|---|
| GHSA-wwwm-px48-fpvq / CVE-2026-48793 | Earlier than 10.11.10 | 10.11.10 | High-severity FFmpeg argument injection through an unescaped subtitle file path. An attacker able to place a crafted filename in a library path could reach arbitrary file write and information disclosure. |
| GHSA-f47c-m7gr-q92j / CVE-2026-49246 | Earlier than 10.11.10 | 10.11.10 | Low-severity MKV attachment filename path traversal. A forged attachment filename can redirect subtitle extraction outside the intended attachment path during affected playback behavior. |
The June advisory floor is 10.11.10, not a promise of ongoing security support. Jellyfin's policy supports the most recent stable server release; the 12.1 stable release was published September 15, 2026. Review the complete supported migration path, plugins, clients and recovery requirements before moving from 10.11. Keep exposure contained while qualifying the target; neither an old fixed floor nor a prerelease is a substitute for that decision.
The first advisory is reachable through an unauthenticated subtitle endpoint, but its documented impact still depends on an attacker being able to place a file in a Jellyfin library directory, such as through an overly broad network share or guest upload workflow. The second depends on crafted MKV attachment metadata and playback that extracts or burns subtitles. These conditions make ingest and filesystem permissions part of the response, not an excuse to delay patching.
Interactive Operating Model
The preserved model moves from containment to evidence, remediation, and validation. Each card names an output that belongs in the change or incident record before the next stage begins.
Go, Hold, Recover, or Escalate
| Decision | Required evidence | Action |
|---|---|---|
| Proceed with patch canary | Version and image known, consistent backup available, management retained, no active compromise indication | Patch the least-critical representative target and run acceptance tests |
| Hold and inventory | Version, writable path, ingest owner, plugin, identity, or backup is unknown | Resolve the unknown without playing suspect media |
| Recover clean | Unexpected writes, integrity failure, unauthorized access, unexplained process, token exposure, or lost trust | Keep isolated; rebuild from known-good software and reviewed data |
| Escalate | Evidence, regulated data, notification, insurance, employment, or legal duties may apply | Preserve evidence and engage qualified responders or counsel |
Step 1: Contain Without Destroying Evidence
- Start a UTC incident/change timeline. Record the decision maker, server, access path, current symptom, and each containment action.
- Pause Sonarr, Radarr, download clients, watched folders, guest uploads, and other writers that can place files in Jellyfin libraries. Preserve their logs.
- Do not play, scan, rename, extract, or probe a suspect subtitle or MKV merely to see what happens.
- Remove direct Internet exposure and restrict remote access to a known private management path. Preserve local console or out-of-band access.
- Do not delete files, prune containers, clear logs, run a full library rescan, or rotate every secret before determining whether evidence obligations apply.
If Jellyfin is earlier than 10.11.10 and no compromise is suspected, the operational goal is a prompt controlled patch, not extended exposure while building a perfect test environment. If compromise is suspected, availability and eradication become separate workstreams.
Step 2: Inventory Version, Image, Mounts, and Writers
Use Bash with Docker/Compose and jq available, an authorized account, and the reviewed Compose project path in JELLYFIN_PROJECT_DIR. Confirm that the loopback endpoint is this project and store unredacted output privately. Each example is a fail-fast subshell; an error means stop and investigate. These examples use a Compose service named jellyfin, derive its actual container ID, and have not been run by TechGeeks.
(
set -euo pipefail
umask 077
cd -- "${JELLYFIN_PROJECT_DIR:?Set the reviewed Compose directory}"
date -u +%FT%TZ
docker compose config --quiet
docker compose ps jellyfin
curl -fsS http://127.0.0.1:8096/System/Info/Public \
| jq '{ProductName,Version,OperatingSystem}'
CONTAINER_ID=$(docker compose ps -q jellyfin)
test -n "$CONTAINER_ID"
docker inspect --format '{{.Config.Image}} {{.Image}}' "$CONTAINER_ID"
docker inspect --format '{{range .Mounts}}{{println .Source "->" .Destination "RW=" .RW}}{{end}}' "$CONTAINER_ID"
IMAGE_ID=$(docker inspect --format '{{.Image}}' "$CONTAINER_ID")
docker image inspect "$IMAGE_ID" --format '{{json .RepoDigests}}' | jq .
)
Example expected output (not observed):
2026-08-24T20:00:00Z
jellyfin jellyfin/jellyfin:10.11.9 Up
{"ProductName":"Jellyfin Server","Version":"10.11.9","OperatingSystem":"Linux"}
jellyfin/jellyfin:10.11.9 sha256:<LOCAL_IMAGE_ID>
/srv/jellyfin/config -> /config RW= true
/srv/media/movies -> /media/movies RW= false
[
"jellyfin/jellyfin@sha256:<REGISTRY_DIGEST>"
]
The vulnerable version in that output is illustrative, not an observed TechGeeks host. Record plugins, LDAP/authentication provider, reverse proxy, request/import applications, transcode and temporary directories, subtitle downloaders, metadata writers, service account, host users/groups, network shares, and any path mounted read-write.
Step 3: Preserve Logs, Writes, and a Recovery Copy
Collect logs before restart because container logs may rotate. Treat file paths and output as private: they can reveal titles, users, addresses, tokens, and incident scope.
(
set -euo pipefail
umask 077
cd -- "${JELLYFIN_PROJECT_DIR:?Set the reviewed Compose directory}"
: "${EVIDENCE_ROOT:?Set an existing restricted evidence directory}"
: "${EXPOSURE_START:?Set the incident start time, including UTC timezone}"
: "${MEDIA_ROOT:?Set the verified media filesystem root}"
: "${STATE_ROOT:?Set the verified Jellyfin state filesystem root}"
test -d "$EVIDENCE_ROOT"
test -d "$MEDIA_ROOT"
test -d "$STATE_ROOT"
OUT=$(mktemp -d "$EVIDENCE_ROOT/jellyfin.XXXXXXXX")
docker compose config --quiet
docker compose logs --timestamps --since "$EXPOSURE_START" jellyfin >"$OUT/container.log" 2>&1
TZ=UTC find "$MEDIA_ROOT" -xdev -type f -newermt "$EXPOSURE_START" -printf '%T@ %s %u:%g %m %p\0' >"$OUT/media-timeline.nul"
TZ=UTC find "$STATE_ROOT" -xdev -type f -newermt "$EXPOSURE_START" -printf '%T@ %s %u:%g %m %p\0' >"$OUT/state-timeline.nul"
(cd -- "$OUT" && sha256sum container.log media-timeline.nul state-timeline.nul >SHA256SUMS)
printf 'Private evidence: %s\n' "$OUT"
)
Expected artifacts, not observed results: a unique private directory with retained container logs, NUL-delimited file records and a checksum manifest. These GNU find examples do not cross filesystem boundaries or grant missing read permissions: separately inventory nested mounts, failures, missing/rotated logs and the full exposure window. Live collection is not an immutable forensic image, and timestamps can be misleading or altered. Preserve original evidence through the authorized incident procedure; hashes identify collected bytes, not safety or provenance.
For recovery, Jellyfin 10.11 offers a built-in online backup under Dashboard > Backups; take it during low activity with no scan running. A manual Docker backup requires stopping Jellyfin before copying the persistent /config source. Preserve the original evidence separately from the working recovery copy. Do not restore a potentially compromised configuration into a trusted environment without review.
Step 4: Patch to the Current Stable Release
Resolve the current official image and platform digest. Do not paste a digest from another architecture, registry, or article. A digest pin makes the deployed bytes explicit, but it also prevents automatic receipt of later security fixes; updating the reviewed digest is part of the patch process.
(
set -euo pipefail
: "${JELLYFIN_TAG:?Set the reviewed supported stable image tag}"
IMAGE="jellyfin/jellyfin:$JELLYFIN_TAG"
docker buildx imagetools inspect "$IMAGE"
docker pull "$IMAGE"
docker image inspect "$IMAGE" --format '{{json .RepoDigests}}' | jq .
)
Review and place the correct digest in Compose. This configuration is an unperformed example and must be reconciled with the existing image, UID/GID, GPU devices, network, and paths:
services:
jellyfin:
image: jellyfin/jellyfin@sha256:<REVIEWED_PLATFORM_DIGEST>
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
volumes:
- /srv/jellyfin/config:/config
- /srv/jellyfin/cache:/cache
- /srv/media/movies:/media/movies:ro
- /srv/media/tv:/media/tv:ro
Test capability dropping on a canary because GPU, networking, and host integrations differ. Separate ingest applications can retain narrowly scoped write access to staging or final library paths; Jellyfin playback normally does not need broad write access to source media. If local NFO, artwork, subtitle, or deletion workflows require writes, isolate those paths and document the risk instead of mounting all storage read-write.
(
set -euo pipefail
umask 077
cd -- "${JELLYFIN_PROJECT_DIR:?Set the reviewed Compose directory}"
docker compose config --quiet
docker compose pull jellyfin
docker compose up -d --no-deps jellyfin
docker compose ps jellyfin
curl -fsS http://127.0.0.1:8096/System/Info/Public \
| jq '{ProductName,Version,OperatingSystem}'
docker compose logs --since 10m --timestamps jellyfin
)
Example expected output (not observed): the service is running the reviewed digest, the public system endpoint reports the reviewed target version, startup reaches a ready state without unexplained migration or plugin errors, and the server remains on the private management path.
Step 5 Validation: Positive, Negative, and Recovery Paths
- Version: API-reported version, container image ID, and registry digest agree with the change record.
- Positive playback: Authorized known-good media Direct Plays on one browser and one household client.
- Subtitle path: Known-good text and image subtitles behave as expected; pair the client outcome with the matching FFmpeg log.
- Controlled transcode: A representative file transcodes without an unexpected output path, argument, or write outside configured cache/transcode locations.
- Identity: Non-admin login, administrator login from a private path, and LDAP/plugin login if used all work as intended.
- Library and scans: A controlled scan completes; ingest remains paused until the acceptance gate passes.
- Denied write: The Jellyfin container cannot create a test file in a read-only media mount.
- Recovery: Restore a reviewed backup on an isolated target at the matching patched version and verify users, libraries, clients, and logs.
- Monitoring: A synthetic login/playback or equivalent path alert reaches a monitored channel independent of the Jellyfin host.
Before the canary starts, create and verify an empty synthetic directory named .tg-permission-probe on the test copy of the media filesystem. Confirm the running container sees that directory inside its actual read-only mount. The probe creates a unique empty file only if the restriction unexpectedly fails, then removes only that file. Do not write into real media or use a crafted exploit file.
(
set -euo pipefail
cd -- "${JELLYFIN_PROJECT_DIR:?Set the reviewed Compose directory}"
docker compose exec -T jellyfin sh -c '
test -d /media/movies/.tg-permission-probe || exit 2
if probe=$(mktemp /media/movies/.tg-permission-probe/probe.XXXXXX); then
rm -- "$probe" || exit 3
printf "%s\n" "UNEXPECTED: test directory is writable" >&2
exit 1
fi
printf "%s\n" "Creation failed; inspect the error and mount before classifying the result."
'
)
Example expected output (not observed):
mktemp: ... Read-only file system
Creation failed; inspect the error and mount before classifying the result.
A missing directory, missing mktemp command, or other error is not a passed read-only test. Confirm the recorded failure is a read-only-filesystem denial and verify the mount mapping. That result applies only to the tested mount and container. It does not prove that every config, cache, temporary, network-share, plugin, or host path is constrained.
Rollback and Clean Recovery
Do not roll back below 10.11.10 to recover service; that returns to the advisory's affected range. Jellyfin also has no general downgrade mechanism after database migrations. A retained old image is not sufficient: rollback requires the matching pre-migration data and configuration backup.
- If the reviewed target fails without compromise indicators, keep the service private, preserve new logs, and diagnose image architecture, mounts, permissions, database migration, plugins, GPU, proxy, and client behavior.
- Restore only onto an isolated target using the Jellyfin built-in restore or a stopped manual copy, and run the same patched release unless the official current recovery procedure explicitly requires another fixed version.
- If trust is lost, rebuild the host or container environment from known-good software, inspect data before reintroduction, rotate Jellyfin admin passwords, API keys, LDAP credentials, proxy secrets, and downstream Arr/request tokens from a clean client, and revoke sessions.
- Do not delete the original evidence when the clean rebuild becomes operational.
Troubleshooting by Evidence
| Symptom | First check | Stop condition |
|---|---|---|
| API still reports an older version | Compose image reference, pulled digest, recreated container ID, and proxy cache | Do not reopen access |
| Container starts, UI fails | Startup/migration logs, config ownership, free space, database, and proxy route | Preserve logs before another restart |
| Subtitles fail after patch | Client/version, subtitle type, known-good file, FFmpeg log, and permissions | Do not use suspect media as the test |
| Read-only mount breaks metadata workflow | Which exact feature writes NFO, artwork, subtitles, or deletions | Do not restore broad write access without a scoped design |
| Unexpected file appears outside cache/config | Timestamp, owner, process, path, logs, and ingest writer | Isolate and escalate possible compromise |
| Restore starts but users/libraries differ | Backup origin, server version, same-system boundary, paths, and selected backup components | Do not overwrite the only backup |
Security, Privacy, Legal, and Recovery Boundaries
A fixed release addresses documented code paths; it does not establish that persistence, stolen tokens, altered media, downstream access, or a second vulnerability is absent. Filesystem read-only controls limit one impact path but do not replace patching. Evidence can contain private titles, paths, users, addresses, authentication values, and regulated data. Restrict access, record custody and retention, and publish only reviewed redacted derivatives. Obtain qualified advice for notification, employee monitoring, customer disclosure, insurance, or legal evidence duties.
What the Evidence Does Not Prove
- The advisories do not prove that every pre-10.11.10 host was exploited; they establish affected code and conditions.
- Running a patched release does not prove the host was never compromised or that all current and future Jellyfin vulnerabilities are closed.
- A clean startup and successful stream do not prove plugins, LDAP, reverse proxies, downloaders, network shares, or every client are safe.
- A denied media write does not prove the service account cannot write elsewhere.
- An isolated restore proves only the recorded backup and test target at that time; it does not prove full incident eradication.
- No malformed media, upgrade, rollback, screenshot, or lab result described here has been performed by TechGeeks.
Series Navigation and Related TechGeeks Resources
- Network Security Field Notes: Start Here
- Resilience Planning: Patch, Shield, Replace, or Segment
- 15 Router Security Settings to Audit
- Back Up and Restore Jellyfin and the Media Automation Stack


