plex32-scripts/PLEX32-Optimization-Summary.html

609 lines
25 KiB
HTML
Raw Permalink Normal View History

2026-07-26 21:35:57 -04:00
<!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>