Safer, quieter Plex Media Server maintenance for libraries that live on a network mount (a NAS over SMB/NFS) instead of local disk.
Plex's built-in background tasks were designed for a local disk. Point them at a network share and two things go wrong:
- Constant NAS hammering — analysis tasks read every file over the network, on import, all day. This is why a NAS-backed Plex feels sluggish.
- A real data-loss trap — "Empty trash automatically after every scan" assumes a missing file is really gone. If the network mount drops for a moment during a scan, every file looks missing → the auto-empty can wipe whole libraries.
This toolkit fixes both: a one-shot script to set the right Plex settings, a mount-checked trash-emptier to replace the dangerous auto-empty, and Pushover alerts so you actually know when nightly maintenance runs.
Migrating Plex off a NAS onto dedicated hardware (e.g. an NVIDIA Shield) while keeping the media on the NAS? See the companion project: plex-shield-migrator.
On local disk, a missing file means deleted, and reading files is cheap. On a network mount, a missing file might just mean the mount blipped, and reading files is expensive. So:
- Destructive operations (emptying trash) must be gated on the mount being provably readable, and backed up first.
- Heavy operations (analysis, thumbnails) belong in a scheduled off-peak window, never "as soon as possible," and never reading-every-file when you don't need it.
- You should be able to observe when maintenance runs, so you can confirm it stays off-peak.
| Plex built-in (Settings → ...) | Default behavior | Why it hurts a network-mounted library | Recommended | Handled by |
|---|---|---|---|---|
| Empty trash automatically after every scan | On | A momentary mount drop makes every file look "missing"; the auto-empty then permanently purges them — potentially a whole library | Off (autoEmptyTrash=0) |
safe-empty-trash.sh (mount-gated, backs up first) |
| Generate video preview thumbnails (BIF) | On / scheduled | Reads every video end-to-end over the network — massive sustained I/O | Never (GenerateBIFBehavior=never) |
tune-pms.sh |
| Generate intro markers | As soon as possible | Audio-analyzes every new item on import → NAS hammering | Scheduled (butler window) | tune-pms.sh |
| Generate credits markers | As soon as possible | same | Scheduled | tune-pms.sh |
| Generate ad markers | As soon as possible | reads files on import | Scheduled | tune-pms.sh |
| Generate chapter thumbnails | As soon as possible | reads files on import | Scheduled | tune-pms.sh |
| Analyze audio loudness | As soon as possible | full-file audio analysis over the network | Scheduled | tune-pms.sh |
| Butler maintenance window | Broad | Heavy tasks can run during your peak streaming hours | Off-peak window (e.g. 06:00–09:00) | tune-pms.sh + observe with butler-watch.sh |
| Scan library periodically | On (hourly/6 h) | — | Keep on — file-change events are unreliable over SMB/NFS, so the periodic scan is your real "new media" discovery | leave as-is |
| Backup database | On, scheduled | — | Keep on | safe-empty-trash.sh also forces a fresh backup right before any purge |
The short version: disable BIF, set every analysis task to scheduled, turn auto-empty
off, and put the butler window off-peak. Then empty trash with safe-empty-trash.sh
and watch the window with butler-watch.sh.
| File | Purpose |
|---|---|
tune-pms.sh |
Apply the recommended settings above via the Plex API (dry-run by default; --apply to write). |
safe-empty-trash.sh |
Replace the dangerous auto-empty: empties trash only when Plex can read real files off the mount, and takes a fresh DB backup first. Dry-run by default; --commit to empty. |
butler-watch.sh |
Runs on an always-on host; polls Plex and sends a Pushover push when the nightly maintenance window starts and ends (with duration). |
ensure-watch.sh |
Idempotent launcher for butler-watch.sh (start at boot / self-heal). |
lib/notify.sh |
Fire-and-forget Pushover helper (reads creds from a file or env). |
lib/common.sh |
Shared logging helpers. |
config/maintenance.conf |
Your Plex host + token settings (copy to ….local to keep real values out of git). |
cp config/maintenance.conf config/maintenance.conf.local # then edit PLEX_HOST + token
export PLEX_TOKEN=xxxxxxxx # or set it in the .local file
./tune-pms.sh # dry-run: shows current -> recommended
./tune-pms.sh --apply # write the recommended settings
./safe-empty-trash.sh # dry-run: verifies the mount, empties nothing
./safe-empty-trash.sh --commit # backs up the DB, then empties trash safelyFinding your Plex token: https://support.plex.tv/articles/204059436.
butler-watch.sh pushes you a "maintenance started 🛠 / done ✅ (~N min)" pair each
night. It reads Pushover credentials from ~/.config/plex-notify/pushover-creds.json
({"user":"…","token":"…"}) or the PUSHOVER_USER/PUSHOVER_TOKEN env vars, and is
configured with env vars (PLEX_URL, POLL, DEBOUNCE). Deploy it to your
always-on host (the NAS itself works well) and keep it alive with ensure-watch.sh
from a boot task / cron. See the comments at the top of each script.
bash and curl. The Plex server reachable on your LAN, and a Plex token. Optional:
SSH to the box running Plex (lets the scripts read the token from Preferences.xml
automatically — handy when Plex runs on a Synology NAS).
MIT — see LICENSE.