From b16f83a59d648958816d92dbf77af8946ec4fce5 Mon Sep 17 00:00:00 2001 From: Nikholas Pcenicni <82239765+nikpcenicni@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:37:38 -0500 Subject: [PATCH] Add Jellyfin + macOS: Persistent NFS Mount documentation --- macos/Jellyfin-NFS.md | 259 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 macos/Jellyfin-NFS.md diff --git a/macos/Jellyfin-NFS.md b/macos/Jellyfin-NFS.md new file mode 100644 index 0000000..115a1d2 --- /dev/null +++ b/macos/Jellyfin-NFS.md @@ -0,0 +1,259 @@ +# Jellyfin + macOS: Persistent NFS Mount (Fix for Libraries Randomly “Clearing”) + +This README documents the working fix I applied when Jellyfin (on a Mac mini) periodically “lost” or cleared my Movies/TV libraries that live on a NAS mounted over NFS. + +It includes the exact commands, files, and rationale so I can reproduce it later. + +--- + +## Problem Summary + +- Symptom: Every day or two, Jellyfin showed empty Movies/TV libraries. +- Media location: NFS share at `/Volumes/media` from NAS `192.168.50.105:/media`. +- Root cause: macOS was using autofs (`/- /etc/auto_nfs`). autofs can unmount after inactivity or brief network blips. When the mount disappears during a Jellyfin scan/file-watch, Jellyfin sees files as missing and removes them from its DB. + +## Solution Summary + +- Stop using autofs for this path. +- Create a persistent mount at boot using a LaunchDaemon and a small network‑aware mount script. +- The script: + - Is idempotent: does nothing if already mounted. + - Checks NAS reachability first. + - Logs to `/var/log/mount_media.(out|err)`. + - Optionally restarts Jellyfin (Homebrew service) if the mount comes back. + +--- + +## Prerequisites / Assumptions + +- macOS with admin (sudo) access. +- NFS server: `192.168.50.105` exporting `/media` (adjust as needed). +- Mount point: `/Volumes/media` (adjust as needed). +- Jellyfin installed (optional Homebrew service restart in script). + +> Tip: If your NAS requires privileged source ports for NFSv4, `resvport` helps. The script falls back to `noresvport` if needed. + +--- + +## Steps (copy/paste commands) + +### 1) Disable autofs for this path and unmount any automounted share + +``` +# Backup and comment out the direct map for NFS +sudo cp /etc/auto_master /etc/auto_master.bak.$(date +%F_%H%M%S) +sudo sed -i.bak 's|^/- /etc/auto_nfs|#/- /etc/auto_nfs|' /etc/auto_master + +# Reload automountd (will unmount /Volumes/media if it was automounted) +sudo automount -vc + +# Ensure the mountpoint is not currently mounted (ignore errors if already unmounted) +sudo umount /Volumes/media 2>/dev/null || sudo umount -f /Volumes/media 2>/dev/null || true +``` + +> Note: If `chown`/`chmod` say “Operation not permitted,” the path is still mounted (or your NAS has root-squash). Unmount first. + +--- + +### 2) Create the network‑aware mount script + +``` +sudo mkdir -p /usr/local/sbin + +sudo tee /usr/local/sbin/mount_media_nfs.sh > /dev/null <<'SH' +#!/bin/sh +set -eu +LOG="/var/log/mount_media.out" +ERR="/var/log/mount_media.err" +MOUNT="/Volumes/media" +SERVER="192.168.50.105:/media" +HOST="${SERVER%%:*}" + +# Ensure mountpoint exists +[ -d "$MOUNT" ] || mkdir -p "$MOUNT" + +# If already mounted as NFS, exit quietly +if mount -t nfs | awk '{print $3}' | grep -qx "$MOUNT"; then + echo "$(date) already mounted: $MOUNT" >>"$LOG" + exit 0 +fi + +# Preflight: only try to mount when NFS port is reachable +if ! /usr/bin/nc -G 2 -z "$HOST" 2049 >/dev/null 2>&1; then + echo "$(date) NAS not reachable on 2049, skipping mount" >>"$LOG" + exit 0 +fi + +echo "$(date) mounting $SERVER -> $MOUNT" >>"$LOG" +/sbin/mount -t nfs -o resvport,hard,nfsvers=4.0 "$SERVER" "$MOUNT" >>"$LOG" 2>>"$ERR" || \ +/sbin/mount -t nfs -o noresvport,hard,nfsvers=4.0 "$SERVER" "$MOUNT" >>"$LOG" 2>>"$ERR" + +# Verify mount succeeded via mount(8) +if mount -t nfs | awk '{print $3}' | grep -qx "$MOUNT"; then + echo "$(date) mount OK: $MOUNT" >>"$LOG" + # Optional: restart Jellyfin if installed via Homebrew + if command -v brew >/dev/null 2>&1 && brew services list | grep -q '^jellyfin\b'; then + echo "$(date) restarting Jellyfin (brew services)" >>"$LOG" + brew services restart jellyfin >>"$LOG" 2>>"$ERR" || true + fi +else + echo "$(date) mount FAILED" >>"$ERR" + exit 1 +fi +SH + +sudo chmod 755 /usr/local/sbin/mount_media_nfs.sh +``` + +--- + +### 3) Create the LaunchDaemon (mount at boot, re-check periodically, network‑aware) + +``` +sudo tee /Library/LaunchDaemons/com.local.mountmedia.plist > /dev/null <<'PLIST' + + + + + Label + com.local.mountmedia + ProgramArguments + + /usr/local/sbin/mount_media_nfs.sh + + RunAtLoad + + StartInterval + 300 + KeepAlive + + NetworkState + + + StandardOutPath + /var/log/mount_media.out + StandardErrorPath + /var/log/mount_media.err + + +PLIST + +sudo chown root:wheel /Library/LaunchDaemons/com.local.mountmedia.plist +sudo chmod 644 /Library/LaunchDaemons/com.local.mountmedia.plist +sudo plutil -lint /Library/LaunchDaemons/com.local.mountmedia.plist + +sudo launchctl bootout system /Library/LaunchDaemons/com.local.mountmedia.plist 2>/dev/null || true +sudo launchctl bootstrap system /Library/LaunchDaemons/com.local.mountmedia.plist +sudo launchctl enable system/com.local.mountmedia +sudo launchctl kickstart -k system/com.local.mountmedia +``` + +--- + +### 4) Run once and verify + +``` +# Run once now (idempotent; logs "already mounted" if present) +sudo /usr/local/sbin/mount_media_nfs.sh + +# LaunchDaemon status +sudo launchctl print system/com.local.mountmedia | egrep 'state|last exit|PID' || true + +# Mount status (should NOT say "automounted") +mount | grep " on /Volumes/media " + +# NFS mount parameters +nfsstat -m | sed -n '/\/Volumes\/media/,+12p' + +# Script logs +tail -n 100 /var/log/mount_media.out /var/log/mount_media.err 2>/dev/null || true +``` + +--- + +### 5) Jellyfin settings + +- Temporarily disable “Enable real-time monitoring” for libraries under `/Volumes/media` until you confirm the mount stays stable. +- Then run “Scan Library Files” to repopulate anything previously removed. + +--- + +### 6) Reboot test (recommended) + +``` +sudo shutdown -r now +``` + +After reboot: + +``` +mount | grep " on /Volumes/media " || echo "Not mounted yet" +sudo launchctl print system/com.local.mountmedia | egrep 'state|last exit' || true +tail -n 100 /var/log/mount_media.out /var/log/mount_media.err 2>/dev/null || true +``` + +--- + +## Rationale for Key Choices + +- Persistent mount (LaunchDaemon) instead of autofs: + - autofs can unmount after inactivity; Jellyfin then removes items it thinks are gone. + - LaunchDaemon ensures the mount is present before scans and remains mounted. +- NFS options: + - `hard`: Blocks I/O until server responds, avoiding spurious “file missing” errors. + - `nfsvers=4.0`: Matches typical NAS defaults and the client’s chosen version. + - `resvport` then fallback `noresvport`: Some servers require privileged ports; the script tries both. +- Network preflight: + - Check TCP/2049 reachability to avoid “Network is unreachable” failures (exit code 51) at boot or during link flaps. +- Logging: + - `/var/log/mount_media.out` and `.err` make it easy to correlate with Jellyfin logs. + +--- + +## Troubleshooting + +- “Operation not permitted” when `chown`/`chmod`: + - The path is mounted over NFS, and root-squash likely prevents ownership changes from the client. Unmount first or change ownership on the NAS. +- LaunchDaemon errors: + - Validate plist: `sudo plutil -lint /Library/LaunchDaemons/com.local.mountmedia.plist` + - Service state: `sudo launchctl print system/com.local.mountmedia` +- Mount health: + - `nfsstat -m` should show vers=4.0, hard, resvport/noresvport. +- Network/power: + - Prevent system sleep that drops the NIC; enable “Wake for network access.” + +--- + +## Optional: If you must keep autofs + +Increase the autofs timeout so it doesn’t unmount on brief inactivity (less ideal than the LaunchDaemon approach): + +``` +sudo cp /etc/auto_master /etc/auto_master.bak.$(date +%F_%H%M%S) +sudo sed -i.bak -E 's|^/-[[:space:]]+/etc/auto_nfs$|/- -timeout=604800 /etc/auto_nfs|' /etc/auto_master +sudo automount -vc +``` + +--- + +## Reverting + +To revert to autofs: + +``` +# Stop and remove LaunchDaemon +sudo launchctl bootout system /Library/LaunchDaemons/com.local.mountmedia.plist +sudo rm -f /Library/LaunchDaemons/com.local.mountmedia.plist + +# Restore /etc/auto_master (uncomment direct map) and reload +sudo sed -i.bak 's|^#/- /etc/auto_nfs|/- /etc/auto_nfs|' /etc/auto_master +sudo automount -vc +``` + +--- + +## Notes + +- Change permissions/ownership on the NFS export from the NAS, not the macOS client (root-squash). +- `showmount` may fail against NFSv4-only servers; it’s not needed here. +- Adjust `SERVER`, `MOUNT`, and `StartInterval` to suit your environment. \ No newline at end of file