New owner page Dankserver/Media Metadata View.md: the shape, the two binds and
why Plex keeps the /mnt/entropy container path, the commands, the laws (SSD-only
upper layer, never mount over an unmounted array, never delete through the view
because a whiteout only hides the array's copy, reap after any relayout, inotify
does not cross the merge), the failure signatures, and the rollback.
Jellyfin.md: /data is the view, so Local metadata works; the
"Read-only file system : '/data/…/movie.nfo'" line that used to be expected noise
is now the signature of a missing view. Plex.md: same bind, one fact changed.
Media Relayout.md: two new laws for the metadata that does not follow a rename.
AGENTS.md: runbook-index row plus a summary row. Root index regenerated.
The SSD-backed overlay the two media servers read and write: shape, guards, commands, laws, rollback.
dankserver
media-server
jellyfin
plex
storage
2026-10-30T00:00:00Z
Media Metadata View
/mnt/media is an OverlayFS merge of the media library: the RAID6 array below,
a directory on the NVMe above. It exists so Jellyfin can write Kodi-format
sidecars (movie.nfo, tvshow.nfo, episode.nfo, artwork) that Plex can
read. Sidecar metadata is only useful sitting next to the media file, and the
array is read-only and slow — so the bytes stay there and the writes land on
SSD. One tree to both servers, one copy of the media.
Shape
Item
Value
Lower layer
/mnt/entropy — the RAID6 array, source of truth, never written
Upper layer
/var/lib/media-meta/upper — NVMe; every write lands here
Work dir
/var/lib/media-meta/work — overlayfs internals, same filesystem
Mount point
/mnt/media, unit mnt-media.mount (WantedBy=local-fs.target)
Repo source
dankserver/host/systemd/mnt-media.mount — host copy is generated state
Installer
scripts/media-view.sh — plan / install / verify / status / reap
Written by
Jellyfin only (Plex binds read-only); nothing else in the fleet touches it
Both consumers guard the view with
UnitConfigItem=ConditionPathIsMountPoint=/mnt/media, and scripts/deploy.sh dankserver --service jellyfin|plex calls media-view.sh install + verify
before it restarts either container.
Every path in the PMS database was recorded as /mnt/entropy/...; only the HOST side of the bind moved, so its database still resolves
Commands
scripts/media-view.sh plan # print the resolved shape, touch nothing
sudo scripts/media-view.sh install # create dirs, install the unit, mount, enable
scripts/media-view.sh verify # is /mnt/media really the overlay? (deploy gate)
scripts/media-view.sh status # mount options + upper-layer size/file count
scripts/media-view.sh reap # list ghost metadata (media moved, sidecars left)
sudo scripts/media-view.sh reap --apply # move ghosts to the SSD trash, never delete
--upper-base DIR and --lower DIR relocate the layers; the defaults are the
host values above.
Laws
The upper layer must sit on non-rotational storage.install and verify
refuse via lsblk -dno ROTA if the upper base lives on the array: an overlay
whose upper is the spinners is the original problem with extra steps, and it
silently turns every metadata write back into a seek.
Never mount the view over an unmounted lower.RequiresMountsFor=/mnt/entropy
plus the container ConditionPathIsMountPoint guards: an empty lowerdir
presents an empty library as truth, and a media scan would then clear the
database of everything.
Never delete through /mnt/media. A delete writes an overlayfs whiteout
(.wh.<name>) in the upper layer, which hides the array's copy without
removing it — the file reappears the moment the view is unmounted, and the
upper layer now carries a landmine. Deletions belong on the raw path
(/mnt/entropy), which the relayout tool already treats as the only writer.
reap refuses to remove whiteouts and only warns about them.
A relayout must be followed by reap. Renames happen on the raw path, so
sidecars the view already wrote stay at the old coordinates: a ghost folder
the servers browse and find nothing in. reap lists upper-layer directories
with no counterpart below and moves them (with their subtree) into a dated
.AGENTTRASH/ under the SSD base. It is recursive and file-safe: a sidecar
that exists only above is normal, so only directories are candidates.
File-watcher events do not cross the overlay. Plex's NFO agent and
Jellyfin's real-time monitor see inotify events for writes that land in the
upper layer, not for changes on the array underneath. After any relayout or
externally-written metadata, trigger a library refresh — see
Media Relayout for the refresh procedure.
Failure signatures
Symptom
Meaning
jellyfin.service / plex.service show ConditionPathIsMountPoint=... was not met and stay inactive
/mnt/media is not mounted. Do not start them anyway: scripts/media-view.sh install, then verify.
Jellyfin log: Read-only file system : '/data/Movies/…/movie.nfo'
The container is binding the raw array, not the view — the quadlet drifted back or the view is gone. media-view.sh status.
A folder appears in a media server with artwork/.nfo but no video
Ghost metadata from a relayout. media-view.sh reap.
verify prints refused: /mnt/media is not a mountpoint
Someone made a plain directory there. That is the fail-closed guard working; remove it and install.
Rollback
Revert the commit that pointed the two quadlets at the view and re-deploy both
services — they bind /mnt/entropy read-only again, which is the pre-view
shape. The NVMe upper layer can stay: nothing reads it once the binds are back,
and it holds no state either server needs. Removing the mount alone
(systemctl stop mnt-media.mount) is safe while no container binds it, and the
array is never modified by any of this.
Links
Jellyfin — the metadata writer; its data layout section.
Plex — the read-only consumer; in-place bind-mount history.
Media Relayout — the tool that moves media on the raw path and the reason reap exists.