<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.wilsoz.com/index.php?action=history&amp;feed=atom&amp;title=Services%2FJellyfin</id>
	<title>Services/Jellyfin - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.wilsoz.com/index.php?action=history&amp;feed=atom&amp;title=Services%2FJellyfin"/>
	<link rel="alternate" type="text/html" href="https://wiki.wilsoz.com/index.php?title=Services/Jellyfin&amp;action=history"/>
	<updated>2026-07-06T07:32:27Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://wiki.wilsoz.com/index.php?title=Services/Jellyfin&amp;diff=10&amp;oldid=prev</id>
		<title>Wikibot: Migrated from services/JELLYFIN.md</title>
		<link rel="alternate" type="text/html" href="https://wiki.wilsoz.com/index.php?title=Services/Jellyfin&amp;diff=10&amp;oldid=prev"/>
		<updated>2026-07-05T18:16:19Z</updated>

		<summary type="html">&lt;p&gt;Migrated from services/JELLYFIN.md&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;#039;&amp;#039;As of: 2026-07-05 — migrated from `services/JELLYFIN.md`&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
= Jellyfin — Media Server =&lt;br /&gt;
&lt;br /&gt;
Operations, backup, and recovery guide for stronghold&amp;#039;s Jellyfin install.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Criticality:&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;MEDIUM — Multi-user service.&amp;#039;&amp;#039;&amp;#039; Media files themselves are large and not in scope for cloud backup (rerippable from physical media as a last resort). Server state (DB, metadata, plugins, config, subtitles) IS in scope — losing it means rebuilding the library from scratch, redoing all manual metadata fixes, and losing watch history. This is the only stronghold service used by multiple people.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Jellyfin&amp;#039;&amp;#039;&amp;#039; is the Free Software media server. Stronghold runs it as a native systemd service (not Docker) because of the package&amp;#039;s tight integration with the host (ffmpeg, GPU, file permissions).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What&amp;#039;s special about this install:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Native systemd, not containerised. Updates via &amp;lt;code&amp;gt;apt&amp;lt;/code&amp;gt;, not Watchtower.&lt;br /&gt;
* Metadata/cache/transcode paths have been &amp;#039;&amp;#039;&amp;#039;relocated&amp;#039;&amp;#039;&amp;#039; away from &amp;lt;code&amp;gt;/var/lib/jellyfin&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;/library/jellyfin-server-app/&amp;lt;/code&amp;gt;. The old paths still exist on disk but are stale (untouched since Oct 2025).&lt;br /&gt;
* ~74G of &amp;lt;code&amp;gt;/var/lib/jellyfin/data&amp;lt;/code&amp;gt; is regenerable cache (trickplay, subtitle extraction, internal backups). Only ~16G is genuinely irreplaceable.&lt;br /&gt;
* Uses SQLite — Jellyfin does not support PostgreSQL/MySQL in any non-experimental form as of 10.11.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Current Version:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;10.11.8+ubu2404&amp;lt;/code&amp;gt; (Debian package). Latest is &amp;lt;code&amp;gt;10.11.9&amp;lt;/code&amp;gt; (released 2026-05-21) — pending upgrade; see [Maintenance](#maintenance).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Users:&amp;#039;&amp;#039;&amp;#039; Multi-user — Marcus + family/friends. Auth is local Jellyfin accounts (not Authentik-federated).&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Service Information ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Package&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;lt;code&amp;gt;jellyfin&amp;lt;/code&amp;gt; (Debian, Ubuntu 24.04 repo)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Systemd unit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;lt;code&amp;gt;/usr/lib/systemd/system/jellyfin.service&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Process user&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;lt;code&amp;gt;jellyfin:jellyfin&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Working directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Environment file&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;lt;code&amp;gt;/etc/default/jellyfin&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Binary&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;lt;code&amp;gt;/usr/bin/jellyfin&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;HTTP port (internal)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| 8096&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;HTTPS port (internal)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| 8920 (unused — TLS terminated upstream)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Public URL&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| (whatever your reverse proxy/Tailscale exposes — internal-only by default)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Companion services ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Service&lt;br /&gt;
! Purpose&lt;br /&gt;
! Where&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;logo-cache&amp;lt;/code&amp;gt; (Docker, &amp;lt;code&amp;gt;nginx:alpine&amp;lt;/code&amp;gt;)&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Jellyfin / IPTV support tool.&amp;#039;&amp;#039;&amp;#039; Static HTTP server for the IPTV feed used by Jellyfin Live TV and the Apple TV IPTV client app. Serves: channel logos (30-day immutable cache) + &amp;lt;code&amp;gt;filtered.m3u&amp;lt;/code&amp;gt; playlist + &amp;lt;code&amp;gt;filtered.xml&amp;lt;/code&amp;gt; EPG (no-cache, must-revalidate — updater rewrites every ~3h via atomic rename). Stood up 2026-05-24.&lt;br /&gt;
| Compose: &amp;lt;code&amp;gt;/library/logo-cache-app/&amp;lt;/code&amp;gt; · Listens: &amp;lt;code&amp;gt;0.0.0.0:8079&amp;lt;/code&amp;gt; → container &amp;lt;code&amp;gt;:80&amp;lt;/code&amp;gt; · Mounts: &amp;lt;code&amp;gt;/raid1/Consolidated/IPTV/logos → /usr/share/nginx/html:ro&amp;lt;/code&amp;gt; (logo files) and &amp;lt;code&amp;gt;/raid1/Consolidated/IPTV → /srv/iptv:ro&amp;lt;/code&amp;gt; (parent dir, exposed via nginx &amp;lt;code&amp;gt;alias&amp;lt;/code&amp;gt; for &amp;lt;code&amp;gt;/filtered.m3u&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;/filtered.xml&amp;lt;/code&amp;gt; only — directory bind, not single-file, so atomic-rename updates are picked up live) · Watchtower: enabled&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Data layout (the important table) ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Path&lt;br /&gt;
! Purpose&lt;br /&gt;
! Size&lt;br /&gt;
! Backup?&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/etc/jellyfin/&amp;lt;/code&amp;gt;&lt;br /&gt;
| System config (&amp;lt;code&amp;gt;system.xml&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;network.xml&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;encoding.xml&amp;lt;/code&amp;gt;, users)&lt;br /&gt;
| &amp;lt;1M&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;YES&amp;#039;&amp;#039;&amp;#039; — mounted as &amp;lt;code&amp;gt;/data/jellyfin-etc&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/jellyfin.db&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Core library DB&amp;#039;&amp;#039;&amp;#039; (items, users, playstate)&lt;br /&gt;
| 1.3G&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;YES via SQLite &amp;lt;code&amp;gt;.backup&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;/dumps/&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — live file excluded from Restic&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/playback_reporting.db&amp;lt;/code&amp;gt;&lt;br /&gt;
| Playback history (from plugin)&lt;br /&gt;
| small&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;YES via SQLite &amp;lt;code&amp;gt;.backup&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;/dumps/&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/subtitles/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Extracted/cached subs (&amp;lt;code&amp;gt;.srt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.ass&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.sup&amp;lt;/code&amp;gt;)&lt;br /&gt;
| ~40G&lt;br /&gt;
| YES — re-extraction is slow + lossy&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/collections/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;playlists/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ScheduledTasks/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;livetv/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attachments/&amp;lt;/code&amp;gt;&lt;br /&gt;
| User-curated state&lt;br /&gt;
| &amp;lt;100M&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/plugins/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Installed plugins + their configs&lt;br /&gt;
| 14M&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/root/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Library definitions&lt;br /&gt;
| 7M&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/metadata/&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;STALE&amp;#039;&amp;#039;&amp;#039; (legacy, pre-relocation, last write Oct 2025)&lt;br /&gt;
| 14G&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO — excluded&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/trickplay/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Generated thumbnails for scrub previews&lt;br /&gt;
| 16G&lt;br /&gt;
| NO — scheduled task regenerates&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/backups/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Jellyfin&amp;#039;s own internal backup plugin output&lt;br /&gt;
| 15G&lt;br /&gt;
| NO — we have our own&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/just-in-case/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Leftover from some operation&lt;br /&gt;
| 2G&lt;br /&gt;
| NO — investigate + delete&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/library.db.old&amp;#039;&amp;#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;library.db.bak&amp;#039;&amp;#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;library.db-wal&amp;lt;/code&amp;gt;&lt;br /&gt;
| Pre-10.9 schema consolidation leftovers&lt;br /&gt;
| ~700M&lt;br /&gt;
| NO — safe to delete&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin/data/SQLiteBackups/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Jellyfin&amp;#039;s auto-DB backups (separate from &amp;lt;code&amp;gt;/data/backups&amp;lt;/code&amp;gt;)&lt;br /&gt;
| varies&lt;br /&gt;
| NO — we have our own&lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;/library/jellyfin-server-app/metadata/&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;ACTIVE metadata&amp;#039;&amp;#039;&amp;#039; (images, NFOs, TMDB cache)&lt;br /&gt;
| 28G&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;YES — mounted as &amp;lt;code&amp;gt;/data/jellyfin-server&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/library/jellyfin-server-app/cache/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Image transcodes, web cache&lt;br /&gt;
| 739M&lt;br /&gt;
| NO — excluded&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/library/jellyfin-server-app/transcodes/&amp;lt;/code&amp;gt;&lt;br /&gt;
| On-the-fly transcode working dir&lt;br /&gt;
| 588K&lt;br /&gt;
| NO — excluded&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Confirm relocation in &amp;lt;code&amp;gt;/etc/jellyfin/system.xml&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;CachePath&amp;gt;/library/jellyfin-server-app/cache&amp;lt;/CachePath&amp;gt;&lt;br /&gt;
&amp;lt;MetadataPath&amp;gt;/library/jellyfin-server-app/metadata&amp;lt;/MetadataPath&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Media files (NOT backed up) ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/raid1/Consolidated/Video/Movies/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/raid1/Consolidated/Video/TV/&amp;lt;/code&amp;gt;, etc. — many TB&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Strategy:&amp;#039;&amp;#039;&amp;#039; Not backed up to cloud. Reproducible from physical media (rerip) as last resort. External RAID is the eventual local-only backup plan. Treat the library as effort-recoverable, not byte-recoverable.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Backup ==&lt;br /&gt;
&lt;br /&gt;
=== The cardinal rule ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Never raw-copy a live SQLite WAL DB.&amp;#039;&amp;#039;&amp;#039; A &amp;lt;code&amp;gt;cp jellyfin.db&amp;lt;/code&amp;gt; (or unmodified file backup) while the server is running can yield an inconsistent file that fails to open, or worse, looks fine but is silently corrupt. SQLite&amp;#039;s &amp;lt;code&amp;gt;.backup&amp;lt;/code&amp;gt; command is the only safe online snapshot mechanism.&lt;br /&gt;
&lt;br /&gt;
=== How it works on stronghold ===&lt;br /&gt;
&lt;br /&gt;
Two things conspire to make Jellyfin backup safe:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;host-dump-databases.sh&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; runs as &amp;lt;code&amp;gt;ExecStartPre&amp;lt;/code&amp;gt; for &amp;lt;code&amp;gt;backup.service&amp;lt;/code&amp;gt; (03:00 CST daily). For each SQLite DB it runs:&lt;br /&gt;
   ```sh&lt;br /&gt;
   sqlite3 /var/lib/jellyfin/data/jellyfin.db &amp;quot;.backup &amp;#039;$DUMP_DIR/jellyfin-$DATE.db&amp;#039;&amp;quot;&lt;br /&gt;
   gzip -f &amp;quot;$DUMP_DIR/jellyfin-$DATE.db&amp;quot;&lt;br /&gt;
   ```&lt;br /&gt;
   The &amp;lt;code&amp;gt;.backup&amp;lt;/code&amp;gt; command takes a short shared lock, copies pages atomically, and produces a file that&amp;#039;s identical to a &amp;lt;code&amp;gt;VACUUM INTO&amp;lt;/code&amp;gt;-style snapshot. Jellyfin keeps running throughout.&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;backup-to-hetzner.sh&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; then runs Restic. The live &amp;lt;code&amp;gt;jellyfin.db&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;playback_reporting.db&amp;lt;/code&amp;gt;, and all their &amp;lt;code&amp;gt;-wal&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;-shm&amp;lt;/code&amp;gt; sidecars are &amp;#039;&amp;#039;&amp;#039;excluded&amp;#039;&amp;#039;&amp;#039; so the only DB copy in the snapshot is the consistent one from &amp;lt;code&amp;gt;/dumps/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Restic also excludes the regenerable dirs (trickplay, internal backups, just-in-case, stale metadata path, cache, transcodes).&lt;br /&gt;
&lt;br /&gt;
=== What&amp;#039;s actually in a snapshot ===&lt;br /&gt;
&lt;br /&gt;
Per nightly Restic run for Jellyfin:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Source mount&lt;br /&gt;
! Inside snapshot&lt;br /&gt;
! Approx after exclusions&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/etc/jellyfin&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/data/jellyfin-etc&amp;lt;/code&amp;gt;&lt;br /&gt;
| All config XML&lt;br /&gt;
| &amp;lt;1M&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/var/lib/jellyfin&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/data/jellyfin&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;plugins/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;root/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;data/{subtitles,collections,playlists,ScheduledTasks,livetv,attachments}&amp;lt;/code&amp;gt;&lt;br /&gt;
| ~40G (subtitles dominate)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/library/jellyfin-server-app&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/data/jellyfin-server&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;metadata/&amp;lt;/code&amp;gt;&lt;br /&gt;
| 28G&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/library/backup-stack/dumps/&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/dumps&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;jellyfin-YYYYMMDD.db.gz&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jellyfin-playback-YYYYMMDD.db.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
| ~300M compressed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First snapshot ≈ 70G stored. Daily incrementals tiny thanks to Restic dedup/compression.&lt;br /&gt;
&lt;br /&gt;
=== Verifying the backup ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Confirm today&amp;#039;s SQLite dump was created&lt;br /&gt;
ls -lh /library/backup-stack/dumps/jellyfin-*.db.gz&lt;br /&gt;
&lt;br /&gt;
# Spot-check dump integrity (extract + open)&lt;br /&gt;
gunzip -k /library/backup-stack/dumps/jellyfin-$(date +%Y%m%d).db.gz&lt;br /&gt;
sqlite3 /tmp/jellyfin-test.db &amp;quot;PRAGMA integrity_check;&amp;quot;  # expect &amp;quot;ok&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Confirm Restic snapshot contains the right paths&lt;br /&gt;
docker exec restic-backup restic snapshots --latest 1&lt;br /&gt;
docker exec restic-backup restic ls latest | grep -E &amp;quot;^/data/jellyfin-(etc|server)?&amp;quot; | head -20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Recovery ==&lt;br /&gt;
&lt;br /&gt;
=== Scenario A: corrupt or lost &amp;lt;code&amp;gt;jellyfin.db&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
The DB is the most fragile and most critical piece. Recovery in order of cost:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Use the live &amp;lt;code&amp;gt;data/SQLiteBackups/&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; if it&amp;#039;s recent and intact — Jellyfin keeps its own rolling SQLite copies here.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Restore from &amp;lt;code&amp;gt;/library/backup-stack/dumps/jellyfin-YYYYMMDD.db.gz&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; (today&amp;#039;s dump, lives locally on stronghold).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Restore from Restic&amp;#039;&amp;#039;&amp;#039; (Hetzner) if local dumps are gone too.&lt;br /&gt;
&lt;br /&gt;
Procedure (option 2 or 3):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl stop jellyfin&lt;br /&gt;
&lt;br /&gt;
# Move aside the broken DB&lt;br /&gt;
sudo mv /var/lib/jellyfin/data/jellyfin.db{,.broken-$(date +%s)}&lt;br /&gt;
sudo rm -f /var/lib/jellyfin/data/jellyfin.db-{wal,shm}&lt;br /&gt;
&lt;br /&gt;
# From local dump:&lt;br /&gt;
sudo gunzip -c /library/backup-stack/dumps/jellyfin-YYYYMMDD.db.gz \&lt;br /&gt;
  | sudo tee /var/lib/jellyfin/data/jellyfin.db &amp;gt;/dev/null&lt;br /&gt;
sudo chown jellyfin:jellyfin /var/lib/jellyfin/data/jellyfin.db&lt;br /&gt;
&lt;br /&gt;
# OR from Restic:&lt;br /&gt;
docker exec restic-backup restic restore latest \&lt;br /&gt;
  --target /tmp/jellyfin-restore \&lt;br /&gt;
  --include /dumps/jellyfin-YYYYMMDD.db.gz&lt;br /&gt;
sudo gunzip -c /tmp/jellyfin-restore/dumps/jellyfin-*.db.gz \&lt;br /&gt;
  | sudo tee /var/lib/jellyfin/data/jellyfin.db &amp;gt;/dev/null&lt;br /&gt;
sudo chown jellyfin:jellyfin /var/lib/jellyfin/data/jellyfin.db&lt;br /&gt;
&lt;br /&gt;
sudo systemctl start jellyfin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Scenario B: full server state loss ===&lt;br /&gt;
&lt;br /&gt;
Rebuild order:&lt;br /&gt;
# Install &amp;lt;code&amp;gt;jellyfin&amp;lt;/code&amp;gt; package, &amp;#039;&amp;#039;&amp;#039;do not start&amp;#039;&amp;#039;&amp;#039; the service yet.&lt;br /&gt;
# Restic restore &amp;lt;code&amp;gt;/data/jellyfin-etc/&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/etc/jellyfin/&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Restic restore &amp;lt;code&amp;gt;/data/jellyfin/&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/var/lib/jellyfin/&amp;lt;/code&amp;gt; (minus the excluded paths — they&amp;#039;ll regenerate).&lt;br /&gt;
# Restic restore &amp;lt;code&amp;gt;/data/jellyfin-server/&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;/library/jellyfin-server-app/&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Restore &amp;lt;code&amp;gt;jellyfin.db&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;playback_reporting.db&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/dumps/&amp;lt;/code&amp;gt; per Scenario A.&lt;br /&gt;
# &amp;lt;code&amp;gt;chown -R jellyfin:jellyfin /var/lib/jellyfin /library/jellyfin-server-app&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;lt;code&amp;gt;systemctl start jellyfin&amp;lt;/code&amp;gt;. First start will regenerate trickplay, transcode dirs, etc.&lt;br /&gt;
# Re-scan libraries from web UI (idempotent — won&amp;#039;t re-import existing items).&lt;br /&gt;
&lt;br /&gt;
=== Scenario C: media library on /raid1 is gone ===&lt;br /&gt;
&lt;br /&gt;
Server state survives independently. Restore server state per Scenario B; then:&lt;br /&gt;
* Media has to come back from physical media (rerip) or external RAID if one exists.&lt;br /&gt;
* The library DB still references the original paths, so once the files reappear at the same paths, items light back up. No re-import needed.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Health checks ==&lt;br /&gt;
&lt;br /&gt;
Add as a weekly cron / systemd timer (see [TODO](#known-issues--tracked-work)):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sqlite3 /var/lib/jellyfin/data/jellyfin.db &amp;quot;PRAGMA integrity_check;&amp;quot;&lt;br /&gt;
sqlite3 /var/lib/jellyfin/data/playback_reporting.db &amp;quot;PRAGMA integrity_check;&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Any output other than &amp;lt;code&amp;gt;ok&amp;lt;/code&amp;gt; → page Matrix.&lt;br /&gt;
&lt;br /&gt;
Also useful, opportunistically:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# What&amp;#039;s holding the DB right now (should be ONLY the jellyfin process)&lt;br /&gt;
sudo lsof /var/lib/jellyfin/data/jellyfin.db&lt;br /&gt;
&lt;br /&gt;
# Confirm WAL is on (it should always say &amp;quot;wal&amp;quot;)&lt;br /&gt;
sudo sqlite3 /var/lib/jellyfin/data/jellyfin.db &amp;quot;PRAGMA journal_mode;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Lock-error counts in the last day&lt;br /&gt;
sudo grep -c &amp;quot;database table is locked&amp;quot; /var/log/jellyfin/log_$(date +%Y%m%d).log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
=== Apt update path ===&lt;br /&gt;
Jellyfin updates via the Ubuntu apt repo, &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; Watchtower (it&amp;#039;s not a container).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Check current vs latest&lt;br /&gt;
apt list --installed 2&amp;gt;/dev/null | grep jellyfin&lt;br /&gt;
# Latest tag: https://api.github.com/repos/jellyfin/jellyfin/releases/latest&lt;br /&gt;
&lt;br /&gt;
# Upgrade pattern (always snapshot DB first):&lt;br /&gt;
sudo systemctl stop jellyfin&lt;br /&gt;
sudo sqlite3 /var/lib/jellyfin/data/jellyfin.db &amp;quot;.backup &amp;#039;/library/backup-stack/dumps/jellyfin-pre-upgrade-$(date +%Y%m%d).db&amp;#039;&amp;quot;&lt;br /&gt;
sudo apt update &amp;amp;&amp;amp; sudo apt install --only-upgrade jellyfin jellyfin-server jellyfin-web&lt;br /&gt;
sudo systemctl start jellyfin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Scheduled tasks worth knowing about ===&lt;br /&gt;
Visible in the web UI at &amp;lt;code&amp;gt;Dashboard → Scheduled Tasks&amp;lt;/code&amp;gt;. Two that matter:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Extract Subtitles&amp;#039;&amp;#039;&amp;#039; — runs daily, can cause &amp;lt;code&amp;gt;UserData&amp;lt;/code&amp;gt; lock contention if it overlaps active playback (see [Gotchas](#gotchas)). Schedule defensively (e.g. 04:30, not 01:00).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Refresh Library&amp;#039;&amp;#039;&amp;#039; — keeps &amp;lt;code&amp;gt;jellyfin.db&amp;lt;/code&amp;gt; warm. Don&amp;#039;t run multiple library refreshes in parallel.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Gotchas ==&lt;br /&gt;
&lt;br /&gt;
=== SQLite &amp;quot;database table is locked&amp;quot; errors are internal, not external ===&lt;br /&gt;
&lt;br /&gt;
If you see &amp;lt;code&amp;gt;SQLite Error 6: &amp;#039;database table is locked: UserData&amp;#039;&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/var/log/jellyfin/log_*.log&amp;lt;/code&amp;gt;:&lt;br /&gt;
* Error code &amp;#039;&amp;#039;&amp;#039;6 is &amp;lt;code&amp;gt;SQLITE_LOCKED&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — same-process shared-cache contention. NOT another OS process holding the file. So it is &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; a backup/cp problem.&lt;br /&gt;
* Code 5 (&amp;lt;code&amp;gt;SQLITE_BUSY&amp;lt;/code&amp;gt;) would indicate external process contention. We don&amp;#039;t see that.&lt;br /&gt;
* Typical cause: &amp;#039;&amp;#039;&amp;#039;PlaybackReporting plugin&amp;#039;&amp;#039;&amp;#039; is in a tight write loop on &amp;lt;code&amp;gt;UserData&amp;lt;/code&amp;gt; for an active session, and &amp;#039;&amp;#039;&amp;#039;Extract Subtitles&amp;#039;&amp;#039;&amp;#039; scheduled task fires at the same time and tries to write the same table.&lt;br /&gt;
* Mitigations:&lt;br /&gt;
## Move &amp;lt;code&amp;gt;Extract Subtitles&amp;lt;/code&amp;gt; schedule off active-viewing hours.&lt;br /&gt;
## Test disabling PlaybackReporting plugin (you lose history but isolate the variable).&lt;br /&gt;
## Upgrade — 10.11.9 (PR #15368 + #16845) explicitly touches UserManager EF Core + user session concurrency logging.&lt;br /&gt;
&lt;br /&gt;
These errors abort the failing transaction cleanly. They do &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; corrupt the DB.&lt;br /&gt;
&lt;br /&gt;
=== Metadata path relocation ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;system.xml&amp;lt;/code&amp;gt; points &amp;lt;code&amp;gt;MetadataPath&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;CachePath&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;/library/jellyfin-server-app/&amp;lt;/code&amp;gt; — not the package default of &amp;lt;code&amp;gt;/var/lib/jellyfin/&amp;lt;/code&amp;gt;. The default-location dirs still exist with stale content from before relocation. &amp;#039;&amp;#039;&amp;#039;Don&amp;#039;t be fooled&amp;#039;&amp;#039;&amp;#039; into thinking &amp;lt;code&amp;gt;/var/lib/jellyfin/metadata&amp;lt;/code&amp;gt; is the live one; check &amp;lt;code&amp;gt;system.xml&amp;lt;/code&amp;gt; to confirm. Both dirs are listed in the data-layout table above with their actual roles.&lt;br /&gt;
&lt;br /&gt;
=== The &amp;lt;code&amp;gt;.sup&amp;lt;/code&amp;gt; subtitle files ===&lt;br /&gt;
&lt;br /&gt;
PGS (bitmap) subs from Blu-rays. Most are 5–30MB, but some legitimately exceed 100MB or 1GB — e.g. &amp;#039;&amp;#039;&amp;#039;animated sing-along subtitles&amp;#039;&amp;#039;&amp;#039; (Wicked&amp;#039;s &amp;lt;code&amp;gt;.sup&amp;lt;/code&amp;gt; is 1.1GB and intentional). &amp;#039;&amp;#039;&amp;#039;Don&amp;#039;t auto-delete by size.&amp;#039;&amp;#039;&amp;#039; Look up the title via &amp;lt;code&amp;gt;BaseItems.Id&amp;lt;/code&amp;gt; matching the dir hash (dashed-uppercase GUID) before deleting any oversized &amp;lt;code&amp;gt;.sup&amp;lt;/code&amp;gt;. Genuine broken extractions can go; Jellyfin re-extracts on demand.&lt;br /&gt;
&lt;br /&gt;
=== DB lookups ===&lt;br /&gt;
The on-disk subtitle dir name like &amp;lt;code&amp;gt;da/dab167ae-31c1-aa0a-7b64-dd821c44b879/4.sup&amp;lt;/code&amp;gt; maps to a &amp;lt;code&amp;gt;BaseItems.Id&amp;lt;/code&amp;gt;. The DB stores it dashed and uppercase:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo sqlite3 /var/lib/jellyfin/data/jellyfin.db \&lt;br /&gt;
  &amp;quot;SELECT Name, Type, Path FROM BaseItems WHERE Id=&amp;#039;DAB167AE-31C1-AA0A-7B64-DD821C44B879&amp;#039;;&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Jellyfin&amp;#039;s own backup plugin ===&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s a built-in Backup plugin that writes to &amp;lt;code&amp;gt;data/backups/&amp;lt;/code&amp;gt;. We &amp;#039;&amp;#039;&amp;#039;don&amp;#039;t&amp;#039;&amp;#039;&amp;#039; rely on it — our own &amp;lt;code&amp;gt;.backup&amp;lt;/code&amp;gt; dumps are simpler to reason about. The &amp;lt;code&amp;gt;data/backups/&amp;lt;/code&amp;gt; dir is excluded from Restic for that reason. If you ever want to use the built-in plugin instead, flip the exclusion and update this doc.&lt;br /&gt;
&lt;br /&gt;
=== Stale &amp;lt;code&amp;gt;library.db.*&amp;lt;/code&amp;gt; files ===&lt;br /&gt;
&lt;br /&gt;
Jellyfin 10.9 consolidated multiple SQLite DBs into a single &amp;lt;code&amp;gt;jellyfin.db&amp;lt;/code&amp;gt;. The leftover &amp;lt;code&amp;gt;library.db.old&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;library.db.bak1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;library.db-wal&amp;lt;/code&amp;gt; (~700M total) under &amp;lt;code&amp;gt;/var/lib/jellyfin/data/&amp;lt;/code&amp;gt; are safe to delete after confirming Jellyfin starts cleanly without them — Jellyfin will not reach for them again.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Known issues / tracked work ==&lt;br /&gt;
&lt;br /&gt;
See &amp;lt;code&amp;gt;TODO.md&amp;lt;/code&amp;gt; § Jellyfin for the live list. Current outstanding items:&lt;br /&gt;
* [ ] Investigate + delete oversized &amp;lt;code&amp;gt;.sup&amp;lt;/code&amp;gt; files that are genuinely broken (size alone isn&amp;#039;t a signal; manual eyeball required).&lt;br /&gt;
* [ ] Delete pre-10.9 schema cruft (&amp;lt;code&amp;gt;library.db.old&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;library.db.bak1&amp;lt;/code&amp;gt;, etc.).&lt;br /&gt;
* [ ] Weekly &amp;lt;code&amp;gt;PRAGMA integrity_check&amp;lt;/code&amp;gt; → Matrix alert on anything other than &amp;lt;code&amp;gt;ok&amp;lt;/code&amp;gt;.&lt;br /&gt;
* [ ] Upgrade to 10.11.9 (UserManager/EF Core fixes; minor, do alongside next routine maintenance window).&lt;br /&gt;
* [ ] Haruhi S01E01 has a nightly subtitle-extraction failure (&amp;lt;code&amp;gt;Unable to get streams for File:...&amp;lt;/code&amp;gt;) — investigate file integrity.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
* Backup stack overview: &amp;lt;code&amp;gt;services/BACKUP_SYSTEM.md&amp;lt;/code&amp;gt;&lt;br /&gt;
* Restic include/exclude patterns: &amp;lt;code&amp;gt;/library/backup-stack/scripts/backup-to-hetzner.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
* SQLite dump invocations: &amp;lt;code&amp;gt;/library/backup-stack/scripts/host-dump-databases.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
* Disaster recovery procedure: &amp;lt;code&amp;gt;DISASTER_RECOVERY.md&amp;lt;/code&amp;gt; (this service&amp;#039;s recovery section above is the authoritative copy)&lt;br /&gt;
* Upstream changelog: https://github.com/jellyfin/jellyfin/releases&lt;/div&gt;</summary>
		<author><name>Wikibot</name></author>
	</entry>
</feed>