plex32-scripts/PLEX32-Optimization-Summary.html
2026-07-26 21:35:57 -04:00

608 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KingDezigns — PLEX32 Optimization Summary</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #f7f6f2;
--surface: #ffffff;
--surface-alt: #f1f0eb;
--border: #e2e0d8;
--border-strong: #c8c5ba;
--text-primary: #1a1917;
--text-secondary: #6b6960;
--text-muted: #9b9890;
--green-bg: #eaf3de;
--green-border: #97c459;
--green-text: #2d5a0e;
--red-bg: #fcebeb;
--red-border: #f09595;
--red-text: #7a1f1f;
--amber-bg: #faeeda;
--amber-border: #fac775;
--amber-text: #633806;
--coral-bg: #faece7;
--coral-border: #f0997b;
--coral-text: #4a1b0c;
--mono: 'IBM Plex Mono', monospace;
--sans: 'IBM Plex Sans', sans-serif;
}
body {
font-family: var(--sans);
font-size: 14px;
background: var(--bg);
color: var(--text-primary);
line-height: 1.7;
padding: 2.5rem 1.5rem;
}
.page { max-width: 860px; margin: 0 auto; }
header {
border-bottom: 1px solid var(--border-strong);
padding-bottom: 1.25rem;
margin-bottom: 2rem;
}
header h1 {
font-family: var(--mono);
font-size: 18px;
font-weight: 500;
letter-spacing: -0.02em;
margin-bottom: 6px;
}
.meta {
font-size: 12px;
color: var(--text-muted);
font-family: var(--mono);
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.section { margin-bottom: 2.25rem; }
.section-title {
font-family: var(--mono);
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 10px;
}
.section-title::before {
content: attr(data-num);
background: var(--text-primary);
color: var(--bg);
font-size: 10px;
width: 18px;
height: 18px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.section-title::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
p { margin-bottom: 0.75rem; color: var(--text-primary); }
p:last-child { margin-bottom: 0; }
.badge-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 1rem; }
.badge {
font-family: var(--mono);
font-size: 11px;
padding: 3px 10px;
border-radius: 3px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text-secondary);
}
.badge.red { background: var(--red-bg); color: var(--red-text); border-color: var(--red-border); }
.badge.green { background: var(--green-bg); color: var(--green-text); border-color: var(--green-border); }
.badge.amber { background: var(--amber-bg); color: var(--amber-text); border-color: var(--amber-border); }
.step {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
padding: 1rem 1.25rem;
margin-bottom: 0.6rem;
}
.step-header {
font-family: var(--mono);
font-size: 12px;
font-weight: 500;
color: var(--text-secondary);
margin-bottom: 0.6rem;
display: flex;
align-items: center;
gap: 8px;
}
.step-num {
background: var(--surface-alt);
border: 1px solid var(--border);
border-radius: 50%;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 10px;
flex-shrink: 0;
color: var(--text-muted);
}
pre {
background: var(--surface-alt);
border: 1px solid var(--border);
border-radius: 4px;
padding: 10px 12px;
font-family: var(--mono);
font-size: 12px;
overflow-x: auto;
margin: 0.5rem 0;
white-space: pre;
line-height: 1.6;
}
code {
font-family: var(--mono);
font-size: 12px;
background: var(--surface-alt);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid var(--border);
}
.why {
font-size: 12px;
color: var(--text-muted);
margin-top: 0.4rem;
padding-left: 2px;
}
.why::before { content: '↳ why: '; font-family: var(--mono); }
.result { font-size: 12px; margin-top: 0.35rem; padding-left: 2px; }
.result.pass { color: var(--green-text); }
.result.pass::before { content: '✓ '; font-family: var(--mono); }
.result.fail { color: var(--red-text); }
.result.fail::before { content: '✗ '; font-family: var(--mono); }
.result.neutral { color: var(--text-secondary); }
.result.neutral::before { content: '→ '; font-family: var(--mono); }
.callout { border-radius: 6px; padding: 1rem 1.25rem; margin-bottom: 0.75rem; }
.callout.coral { background: var(--coral-bg); border: 1px solid var(--coral-border); }
.callout.coral p { color: var(--coral-text); }
.callout.coral pre { background: #fdf0eb; border-color: var(--coral-border); }
.callout.amber { background: var(--amber-bg); border: 1px solid var(--amber-border); }
.callout.amber p { color: var(--amber-text); }
.callout.amber pre { background: #fdf5e6; border-color: var(--amber-border); }
.callout.amber code { background: #fdf5e6; border-color: var(--amber-border); color: var(--amber-text); }
.callout.green { background: var(--green-bg); border: 1px solid var(--green-border); }
.callout.green p { color: var(--green-text); }
.callout.green pre { background: #d8efc0; border-color: var(--green-border); }
.clue-label {
font-family: var(--mono);
font-size: 10px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--amber-text);
margin-bottom: 8px;
}
.script-box { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.script-row { padding: 0.75rem 1.25rem; border-bottom: 1px solid var(--border); }
.script-row:last-child { border-bottom: none; }
.script-label { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-muted); margin-bottom: 6px; }
.script-row pre { margin: 0; }
table { width: 100%; border-collapse: collapse; font-size: 13px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
th { text-align: left; padding: 8px 14px; font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); background: var(--surface-alt); border-bottom: 1px solid var(--border); font-weight: 500; }
td { padding: 9px 14px; border-bottom: 1px solid var(--border); vertical-align: top; line-height: 1.5; }
tr:last-child td { border-bottom: none; }
.footer {
font-family: var(--mono);
font-size: 11px;
color: var(--text-muted);
margin-top: 2.5rem;
padding-top: 1rem;
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px;
}
</style>
</head>
<body>
<div class="page">
<header>
<h1>KingDezigns — PLEX32 Optimization &amp; Setup Summary</h1>
<div class="meta">
<span>📅 June 1920, 2026</span>
<span>🖥 PLEX32 — Dell Wyse 5070</span>
<span>⚙️ Ubuntu 24.04</span>
<span>🐳 Docker Compose</span>
<span>☁️ Plex Media Server + Tautulli</span>
</div>
</header>
<!-- SECTION 1 — OVERVIEW -->
<div class="section">
<div class="section-title" data-num="1">Overview</div>
<p>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.</p>
<div class="badge-row">
<span class="badge green">Hardware Transcoding — Enabled</span>
<span class="badge green">Tautulli — Installed</span>
<span class="badge green">Backup — Automated</span>
<span class="badge green">SSH Keys — Configured</span>
</div>
<p>All 7 optimization steps completed successfully. PLEX32 is now fully configured and production-ready.</p>
</div>
<!-- SECTION 2 — STEP 1: HARDWARE TRANSCODING -->
<div class="section">
<div class="section-title" data-num="2">Step 1 — Hardware transcoding (Intel Quick Sync)</div>
<p>The J4105 has Intel Quick Sync but the Plex Docker container had no access to <code>/dev/dri</code> and was silently falling back to software transcoding on every stream.</p>
<div class="step">
<div class="step-header"><span class="step-num">1</span> Confirmed iGPU is exposed on the host</div>
<pre>ls -la /dev/dri</pre>
<p class="result pass">card0 (video, GID 44) and renderD128 (render, GID 993) both present</p>
</div>
<div class="step">
<div class="step-header"><span class="step-num">2</span> Updated docker-compose.yml — added device passthrough and group_add</div>
<pre>devices:
- /dev/dri:/dev/dri
group_add:
- "44"
- "993"</pre>
<p class="why">Container needs both the device node and supplementary group membership to access the GPU</p>
<p class="result pass">docker exec confirmed groups 44 and 993 in container process — /dev/dri visible inside container</p>
</div>
<div class="step">
<div class="step-header"><span class="step-num">3</span> Enabled hardware transcoding in Plex UI</div>
<pre>Settings → Transcoder → Use hardware acceleration when available ✅
Settings → Transcoder → Use hardware-accelerated video encoding ✅</pre>
<p class="why">Both were already checked — confirming the settings were attempting HW transcode but failing silently due to missing device access</p>
<p class="result pass">Dashboard showed: 4K (HEVC Main 10) (hw) / SD (HEVC) — Transcode (hw) — both sides of pipeline on GPU</p>
</div>
<div class="step">
<div class="step-header"><span class="step-num">4</span> Set transcoder temp directory</div>
<pre>Settings → Transcoder → Transcoder temporary directory: /transcode</pre>
<p class="why">Was empty — Plex was writing transcode scratch files to uncontrolled location inside container filesystem</p>
<p class="result pass">Now maps to /opt/plex/transcode on host via existing volume mount</p>
</div>
</div>
<!-- SECTION 3 — STEP 2: DIRECT PLAY -->
<div class="section">
<div class="section-title" data-num="3">Step 2 — Direct Play / Direct Stream tuning</div>
<p>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.</p>
<div class="step">
<div class="step-header"><span class="step-num">1</span> Codec analysis — video</div>
<pre>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</pre>
<p class="result neutral">82+8 hevc,Main 10 / 29 h264,High — library is 75% HEVC 10-bit</p>
</div>
<div class="step">
<div class="step-header"><span class="step-num">2</span> Codec analysis — audio</div>
<pre>find ... | ffprobe ... -select_streams a:0 ... | sort | uniq -c</pre>
<p class="result pass">119 aac — 100% AAC audio across entire library. No TrueHD, no DTS. Audio will never force a transcode due to format incompatibility.</p>
</div>
<div class="step">
<div class="step-header"><span class="step-num">3</span> Live playback test on Roku</div>
<pre>Plex Dashboard → active session while playing on Roku TV</pre>
<p class="result pass">Video: 4K (HEVC Main 10) — Direct Stream ✅ / Audio: English (AAC 5.1) → AC3 Transcode (lightweight, expected for Roku HDMI passthrough)</p>
</div>
<div class="callout green">
<p><strong>Result:</strong> Video never transcodes. Audio does a lightweight AAC→AC3 conversion for Roku surround output. Optimal for this setup.</p>
</div>
</div>
<!-- SECTION 4 — STEP 3 & 4: SONIC RADIO + TAUTULLI -->
<div class="section">
<div class="section-title" data-num="4">Steps 3 &amp; 4 — Sonic Radio + Tautulli</div>
<p><strong>Sonic Radio</strong> 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.</p>
<p><strong>Tautulli</strong> 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.</p>
<div class="script-box">
<div class="script-row">
<div class="script-label">Tautulli added to docker-compose.yml</div>
<pre>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</pre>
</div>
<div class="script-row">
<div class="script-label">Config directory</div>
<pre>/opt/tautulli/config</pre>
</div>
<div class="script-row">
<div class="script-label">Web UI</div>
<pre>http://192.168.150.45:8181</pre>
</div>
<div class="script-row">
<div class="script-label">Notification triggers set</div>
<pre>Plex Server Down + Plex Server Back Up → email to rufus.king@kingdezigns.com</pre>
</div>
</div>
</div>
<!-- SECTION 5 — STEP 5: BACKUP -->
<div class="section">
<div class="section-title" data-num="5">Step 5 — Automated config backup</div>
<p>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.</p>
<div class="callout amber">
<div class="clue-label">⚠ Key issue — rsync over NFS hung indefinitely</div>
<p style="margin-bottom:6px;">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.</p>
<pre>sudo chown -R www-data:ncshare /export/kingdezigns-all/Docker/plex/config
sudo chmod -R 2770 /export/kingdezigns-all/Docker/plex/config</pre>
<p style="margin-top:8px;">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.</p>
</div>
<div class="script-box">
<div class="script-row">
<div class="script-label">Backup script location</div>
<pre>/usr/scripts/plex32/plex32_backup.sh</pre>
</div>
<div class="script-row">
<div class="script-label">Schedule</div>
<pre>0 2 */3 * * /usr/scripts/plex32/plex32_backup.sh # Every 3 days at 2:00 AM</pre>
</div>
<div class="script-row">
<div class="script-label">Plex archive destination (NAS08)</div>
<pre>/export/kingdezigns-all/Docker/plex/config/backups/plex/
plex-config-YYYY-MM-DD.tar.gz (3 copies retained)</pre>
</div>
<div class="script-row">
<div class="script-label">Tautulli archive destination (NAS08)</div>
<pre>/export/kingdezigns-all/Docker/plex/config/backups/tautulli/
tautulli-config-YYYY-MM-DD.tar.gz (3 copies retained)</pre>
</div>
<div class="script-row">
<div class="script-label">Excludes from Plex archive</div>
<pre>Cache / Logs / Crash Reports</pre>
</div>
<div class="script-row">
<div class="script-label">SMTP password file</div>
<pre>/etc/plex32-smtp-pass (chmod 600, owned by rufusking)</pre>
</div>
<div class="script-row">
<div class="script-label">Restore procedure (run on plex32)</div>
<pre>cd ~/docker/plex &amp;&amp; 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 &amp;&amp; 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</pre>
</div>
</div>
</div>
<!-- SECTION 6 — LAUNCHER + SSH KEYS -->
<div class="section">
<div class="section-title" data-num="6">Remote launcher + SSH key authentication</div>
<p>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.</p>
<div class="script-box">
<div class="script-row">
<div class="script-label">Launcher script (KingDezigns001)</div>
<pre>~/scripts/plex32_launcher.sh</pre>
</div>
<div class="script-row">
<div class="script-label">Desktop shortcut (KingDezigns001)</div>
<pre>~/Desktop/PLEX32-Launcher.desktop</pre>
</div>
<div class="script-row">
<div class="script-label">Menu options</div>
<pre>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</pre>
</div>
<div class="script-row">
<div class="script-label">SSH keys configured (no password)</div>
<pre>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</pre>
</div>
<div class="script-row">
<div class="script-label">sudo without password (plex32) — /etc/sudoers.d/rufusking-nopasswd</div>
<pre>rufusking ALL=(ALL) NOPASSWD: /usr/bin/apt-get update, /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status sshd</pre>
</div>
</div>
</div>
<!-- SECTION 7 — STEPS 6 & 7 -->
<div class="section">
<div class="section-title" data-num="7">Steps 6 &amp; 7 — Scheduled maintenance &amp; subtitle handling</div>
<p><strong>Scheduled maintenance</strong> was already configured to run between 2:00 AM and 5:00 AM. The following library analysis tasks were confirmed enabled as scheduled tasks:</p>
<table>
<thead>
<tr><th>Task</th><th>Setting</th></tr>
</thead>
<tbody>
<tr><td>Generate video preview thumbnails</td><td>As a scheduled task (changed from Never)</td></tr>
<tr><td>Generate chapter image thumbnails</td><td>As a scheduled task</td></tr>
<tr><td>Analyze audio tracks for loudness</td><td>As a scheduled task</td></tr>
<tr><td>Generate intro video markers</td><td>As a scheduled task and when media is added</td></tr>
<tr><td>Generate credits video markers</td><td>As a scheduled task and when media is added</td></tr>
<tr><td>Analyze audio tracks for sonic features</td><td>As a scheduled task</td></tr>
<tr><td>Generate voice activity data</td><td>As a scheduled task (changed from Never)</td></tr>
</tbody>
</table>
<p style="margin-top:1rem;"><strong>Subtitle handling:</strong> 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 <code>Manually selected</code> — subtitles only appear when explicitly chosen, preventing accidental burn-in transcoding.</p>
</div>
<!-- SECTION 8 — LESSONS LEARNED -->
<div class="section">
<div class="section-title" data-num="8">Lessons learned</div>
<table>
<thead>
<tr><th>Lesson</th><th>Detail</th></tr>
</thead>
<tbody>
<tr>
<td>GPU device passthrough requires group membership</td>
<td>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.</td>
</tr>
<tr>
<td>rsync over NFS is unreliable for large Plex configs</td>
<td>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.</td>
</tr>
<tr>
<td>NAS08 all_squash NFS exports require correct directory ownership</td>
<td>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.</td>
</tr>
<tr>
<td>Scripts running as non-root need sudo for apt-get and fail2ban-client</td>
<td>Both commands require root access. Grant specific NOPASSWD sudo rights via /etc/sudoers.d/ rather than running the whole script as root.</td>
</tr>
<tr>
<td>SSH keys should be set up on all servers from the start</td>
<td>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.</td>
</tr>
<tr>
<td>Hardware transcoding silently falls back without /dev/dri access</td>
<td>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.</td>
</tr>
<tr>
<td>AAC audio across the entire library is ideal</td>
<td>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.</td>
</tr>
</tbody>
</table>
</div>
<!-- SECTION 9 — DOCS TO UPDATE -->
<div class="section">
<div class="section-title" data-num="9">Network docs — items to update</div>
<table>
<thead>
<tr><th>Item</th><th>Old value</th><th>New value</th></tr>
</thead>
<tbody>
<tr>
<td>server_plex32.md — docker-compose.yml</td>
<td>No devices or group_add block</td>
<td>devices: /dev/dri + group_add: ["44","993"] + Tautulli service added</td>
</tr>
<tr>
<td>server_plex32.md — Scheduled Jobs</td>
<td>2 cron jobs (update check, health report)</td>
<td>3 cron jobs — add: <code>0 2 */3 * * /usr/scripts/plex32/plex32_backup.sh</code></td>
</tr>
<tr>
<td>server_plex32.md — new scripts section</td>
<td>No backup script</td>
<td>plex32_backup.sh — tar + NFS copy to NAS08, 3 copies retained, HTML email report</td>
</tr>
<tr>
<td>server_plex32.md — SSH keys</td>
<td>Not documented</td>
<td>PLEX32 has SSH key to NAS08 (192.168.150.35) — required for backup disk stats</td>
</tr>
<tr>
<td>server_plex32.md — sudoers</td>
<td>Not documented</td>
<td>/etc/sudoers.d/rufusking-nopasswd — apt-get update + fail2ban-client commands</td>
</tr>
<tr>
<td>server_plex32.md — Tautulli</td>
<td>Not present</td>
<td>Tautulli running on port 8181, config at /opt/tautulli/config, Plex down/up alerts active</td>
</tr>
<tr>
<td>network_index.md — KingDezigns001 tools</td>
<td>Not documented</td>
<td>plex32_launcher.sh at ~/scripts/ + PLEX32-Launcher.desktop on Desktop — SSH keys to all VLAN 50 servers</td>
</tr>
<tr>
<td>server_nas08.md — NFS export permissions</td>
<td>Not documented</td>
<td>/export/kingdezigns-all/Docker/plex/config — www-data:ncshare 2770, backups/plex and backups/tautulli subdirs created</td>
</tr>
</tbody>
</table>
</div>
<div class="footer">
<span>KingDezigns Home Network</span>
<span>Generated June 20, 2026</span>
</div>
</div>
</body>
</html>