PLEX32 was recently migrated from NAS08 to a dedicated Dell Wyse 5070 (Intel Celeron J4105, 8GB RAM) to support Plex Pass features including Sonic Radio that require x86 CPU capabilities. This session covered a full 7-step optimization of the new server — hardware transcoding, playback tuning, Tautulli setup, automated backup, scheduled maintenance, and subtitle handling.
All 7 optimization steps completed successfully. PLEX32 is now fully configured and production-ready.
The J4105 has Intel Quick Sync but the Plex Docker container had no access to /dev/dri and was silently falling back to software transcoding on every stream.
ls -la /dev/dri
card0 (video, GID 44) and renderD128 (render, GID 993) both present
devices: - /dev/dri:/dev/dri group_add: - "44" - "993"
Container needs both the device node and supplementary group membership to access the GPU
docker exec confirmed groups 44 and 993 in container process — /dev/dri visible inside container
Settings → Transcoder → Use hardware acceleration when available ✅ Settings → Transcoder → Use hardware-accelerated video encoding ✅
Both were already checked — confirming the settings were attempting HW transcode but failing silently due to missing device access
Dashboard showed: 4K (HEVC Main 10) (hw) / SD (HEVC) — Transcode (hw) — both sides of pipeline on GPU
Settings → Transcoder → Transcoder temporary directory: /transcode
Was empty — Plex was writing transcode scratch files to uncontrolled location inside container filesystem
Now maps to /opt/plex/transcode on host via existing volume mount
Library codec analysis revealed 90 HEVC Main 10 files (~75%) and 29 H.264 files (~25%), all with AAC audio. Roku TV (Hisense 55R8BX) was tested — video Direct Streams, audio converts AAC → AC3 for HDMI passthrough.
find "/mnt/plex/media/04 - Videos/Movies" \( -iname "*.mkv" -o -iname "*.mp4" \) | while read -r f; do ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,profile \ -of csv=p=0 "$f" 2>/dev/null done | sort | uniq -c | sort -rn
82+8 hevc,Main 10 / 29 h264,High — library is 75% HEVC 10-bit
find ... | ffprobe ... -select_streams a:0 ... | sort | uniq -c
119 aac — 100% AAC audio across entire library. No TrueHD, no DTS. Audio will never force a transcode due to format incompatibility.
Plex Dashboard → active session while playing on Roku TV
Video: 4K (HEVC Main 10) — Direct Stream ✅ / Audio: English (AAC 5.1) → AC3 Transcode (lightweight, expected for Roku HDMI passthrough)
Result: Video never transcodes. Audio does a lightweight AAC→AC3 conversion for Roku surround output. Optimal for this setup.
Sonic Radio was confirmed working — the whole reason for migrating to PLEX32. Opening any artist in the Music library and clicking Live Radio plays that artist then branches into similar artists using AI-driven discovery. Requires x86 CPU — confirmed working on J4105.
Tautulli was installed as a second service in the existing docker-compose.yml, running on port 8181. Connected to Plex, email notification configured for Plex server down/up alerts using existing Zoho SMTP credentials.
tautulli:
image: lscr.io/linuxserver/tautulli:latest
container_name: tautulli
environment:
- PUID=1000
- PGID=1001
- TZ=America/New_York
volumes:
- /opt/tautulli/config:/config
ports:
- 8181:8181
restart: unless-stopped
/opt/tautulli/config
http://192.168.150.45:8181
Plex Server Down + Plex Server Back Up → email to rufus.king@kingdezigns.com
A backup script was created to archive Plex and Tautulli configs to NAS08 via NFS every 3 days. rsync was attempted first but failed due to NFS directory scanning performance issues and permission constraints from NAS08's all_squash export. The final approach uses tar locally then a single cp to NFS.
rsync hung at "sending incremental file list" because scanning thousands of small Plex metadata files over NFS was extremely slow. Additionally NAS08's all_squash NFS export (anonuid=33/anongid=1001) prevented rufusking on PLEX32 from writing new files to the destination until directory permissions were corrected on NAS08.
sudo chown -R www-data:ncshare /export/kingdezigns-all/Docker/plex/config sudo chmod -R 2770 /export/kingdezigns-all/Docker/plex/config
Final solution: tar to /tmp locally (no NFS), then single cp to NFS. NFS write speed confirmed at 100 MB/s. Total backup runtime ~10 minutes.
/usr/scripts/plex32/plex32_backup.sh
0 2 */3 * * /usr/scripts/plex32/plex32_backup.sh # Every 3 days at 2:00 AM
/export/kingdezigns-all/Docker/plex/config/backups/plex/ plex-config-YYYY-MM-DD.tar.gz (3 copies retained)
/export/kingdezigns-all/Docker/plex/config/backups/tautulli/ tautulli-config-YYYY-MM-DD.tar.gz (3 copies retained)
Cache / Logs / Crash Reports
/etc/plex32-smtp-pass (chmod 600, owned by rufusking)
cd ~/docker/plex && docker compose down cp /mnt/plex/config/backups/plex/plex-config-YYYY-MM-DD.tar.gz /tmp/ sudo rm -rf /opt/plex/config/Library sudo tar -xzf /tmp/plex-config-YYYY-MM-DD.tar.gz -C /opt/plex sudo chown -R rufusking:ncshare /opt/plex/config cd ~/docker/plex && docker compose up -d docker ps # verify both plex and tautulli show Up # Open http://192.168.150.45:32400/web and confirm library intact rm /tmp/plex-config-YYYY-MM-DD.tar.gz
A launcher script was created on KingDezigns001 to remotely execute PLEX32 scripts via SSH with a simple menu. SSH key authentication was configured from KingDezigns001 to all VLAN 50 servers, and from PLEX32 to NAS08, eliminating all password prompts.
~/scripts/plex32_launcher.sh
~/Desktop/PLEX32-Launcher.desktop
1) Update Check → /usr/scripts/plex32/plex32_update_check.sh 2) Health Report → /usr/scripts/plex32/plex32_health_report.sh 3) Plex32 Backup → /usr/scripts/plex32/plex32_backup.sh
KingDezigns001 → PLEX32 (192.168.150.45) KingDezigns001 → NAS08 (192.168.150.35) KingDezigns001 → NAS16 (192.168.150.40) PLEX32 → NAS08 (192.168.150.35) # required for backup disk stats
rufusking ALL=(ALL) NOPASSWD: /usr/bin/apt-get update, /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status sshd
Scheduled maintenance was already configured to run between 2:00 AM and 5:00 AM. The following library analysis tasks were confirmed enabled as scheduled tasks:
| Task | Setting |
|---|---|
| Generate video preview thumbnails | As a scheduled task (changed from Never) |
| Generate chapter image thumbnails | As a scheduled task |
| Analyze audio tracks for loudness | As a scheduled task |
| Generate intro video markers | As a scheduled task and when media is added |
| Generate credits video markers | As a scheduled task and when media is added |
| Analyze audio tracks for sonic features | As a scheduled task |
| Generate voice activity data | As a scheduled task (changed from Never) |
Subtitle handling: Library contains 1168 SRT (safe, no transcode) and 279 PGS image-based subtitles (burn-in forces transcode if selected). Since subtitles are used occasionally and Quick Sync handles the transcode efficiently, Option B (bulk PGS→SRT conversion) was not pursued. Subtitle Mode confirmed set to Manually selected — subtitles only appear when explicitly chosen, preventing accidental burn-in transcoding.
| Lesson | Detail |
|---|---|
| GPU device passthrough requires group membership | Adding /dev/dri to Docker devices is not enough — the container process also needs supplementary groups matching the video (44) and render (993) GIDs on the host. Use group_add in docker-compose. |
| rsync over NFS is unreliable for large Plex configs | Thousands of small metadata files cause rsync to hang indefinitely building the file list over NFS. Use tar locally then a single cp to NFS instead — much faster and more reliable. |
| NAS08 all_squash NFS exports require correct directory ownership | Even sudo on PLEX32 cannot create files on NFS mounts with all_squash unless the destination directory on NAS08 is owned by www-data:ncshare with 2770 permissions. |
| Scripts running as non-root need sudo for apt-get and fail2ban-client | Both commands require root access. Grant specific NOPASSWD sudo rights via /etc/sudoers.d/ rather than running the whole script as root. |
| SSH keys should be set up on all servers from the start | Key authentication eliminates password prompts for both interactive SSH and script-to-script SSH calls (e.g. PLEX32 → NAS08 for disk stats). Generate once per machine, copy to all targets. |
| Hardware transcoding silently falls back without /dev/dri access | Plex showed hardware transcoding as "enabled" in settings but was silently using software transcoding. Always verify with the Dashboard showing (hw) next to the codec during an active transcode. |
| AAC audio across the entire library is ideal | TrueHD and DTS-HD MA are the common culprits that force audio transcoding on most clients. A library with 100% AAC will never trigger an audio-forced transcode regardless of client. |
| Item | Old value | New value |
|---|---|---|
| server_plex32.md — docker-compose.yml | No devices or group_add block | devices: /dev/dri + group_add: ["44","993"] + Tautulli service added |
| server_plex32.md — Scheduled Jobs | 2 cron jobs (update check, health report) | 3 cron jobs — add: 0 2 */3 * * /usr/scripts/plex32/plex32_backup.sh |
| server_plex32.md — new scripts section | No backup script | plex32_backup.sh — tar + NFS copy to NAS08, 3 copies retained, HTML email report |
| server_plex32.md — SSH keys | Not documented | PLEX32 has SSH key to NAS08 (192.168.150.35) — required for backup disk stats |
| server_plex32.md — sudoers | Not documented | /etc/sudoers.d/rufusking-nopasswd — apt-get update + fail2ban-client commands |
| server_plex32.md — Tautulli | Not present | Tautulli running on port 8181, config at /opt/tautulli/config, Plex down/up alerts active |
| network_index.md — KingDezigns001 tools | Not documented | plex32_launcher.sh at ~/scripts/ + PLEX32-Launcher.desktop on Desktop — SSH keys to all VLAN 50 servers |
| server_nas08.md — NFS export permissions | Not documented | /export/kingdezigns-all/Docker/plex/config — www-data:ncshare 2770, backups/plex and backups/tautulli subdirs created |