469 lines
20 KiB
Markdown
469 lines
20 KiB
Markdown
|
|
# PLEX32 — Plex Media Server
|
|||
|
|
Device Type: **Dell Wyse 5070 Thin Client**
|
|||
|
|
Hostname: **plex32**
|
|||
|
|
IP Address: **192.168.150.45**
|
|||
|
|
VLAN: **50 — Lab / Servers**
|
|||
|
|
|
|||
|
|
Last Updated: 2026-07-12
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧩 Role & Purpose
|
|||
|
|
PLEX32 is the **dedicated Plex Media Server** for the KingDezigns network.
|
|||
|
|
It replaced Plex on NAS08, migrating the service to a more capable x86 machine that supports advanced Plex Pass features including **Sonic Radio** — an AI-driven music playlist and discovery feature that requires CPU capabilities not available on Raspberry Pi hardware.
|
|||
|
|
|
|||
|
|
All media files remain stored on NAS08 and are accessed via NFS mounts. PLEX32 handles only transcoding and Plex server logic — it does not store media locally.
|
|||
|
|
|
|||
|
|
This device is the **single Plex Media Server** for the KingDezigns network and is publicly accessible via `plex.kingdezigns.com` through NPM on HAS.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🖥️ Hardware
|
|||
|
|
- Dell Wyse 5070 Thin Client
|
|||
|
|
- Intel Celeron J4105 — 1.50GHz (4 cores) — Intel Quick Sync supported
|
|||
|
|
- 8GB RAM
|
|||
|
|
- 32GB eMMC (OS drive — no SMART support, health monitored via filesystem)
|
|||
|
|
- Gigabit Ethernet
|
|||
|
|
|
|||
|
|
### **Storage**
|
|||
|
|
| Device | Type | Capacity | Role |
|
|||
|
|
|--------|------|----------|------|
|
|||
|
|
| mmcblk0 | eMMC | 29.1GB | OS / system drive |
|
|||
|
|
| /mnt/plex/config | NFS (NAS08) | — | Plex config backup destination only — not used by container |
|
|||
|
|
| /mnt/plex/transcode | NFS (NAS08) | — | Transcode scratch (local at /opt/plex/transcode) |
|
|||
|
|
| /mnt/plex/media | NFS (NAS08) | 1.8TB | Media library |
|
|||
|
|
|
|||
|
|
> Note: Plex config and transcode directories are stored **locally** at `/opt/plex/` — not on NFS. The NFS config mount (`/mnt/plex/config`) is used as the backup destination by `plex32_backup.sh`. Only the media library is actively used by the container.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🌐 Network Placement
|
|||
|
|
- VLAN: **50 — Lab / Servers**
|
|||
|
|
- IP: **192.168.150.45**
|
|||
|
|
- Access Type: LAN / Cable
|
|||
|
|
|
|||
|
|
This placement ensures:
|
|||
|
|
- Isolation from trusted user devices
|
|||
|
|
- Access to NAS08 NFS shares over local VLAN 50
|
|||
|
|
- Direct access to Pi‑hole DNS
|
|||
|
|
- Reduced attack surface
|
|||
|
|
- Accessible via NPM reverse proxy on HAS
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📦 Primary Functions
|
|||
|
|
|
|||
|
|
### **Core Services**
|
|||
|
|
- **Plex Media Server** (Docker — linuxserver/plex:latest)
|
|||
|
|
- **Tautulli** (Docker — linuxserver/tautulli:latest) — playback history, monitoring, and alerts
|
|||
|
|
|
|||
|
|
### **Plex Libraries**
|
|||
|
|
| Library | Path in Container | Source on NAS08 |
|
|||
|
|
|---------|------------------|-----------------|
|
|||
|
|
| Music | /media/03 - Music | /export/kingdezigns-public/03 - Music |
|
|||
|
|
| Videos | /media/04 - Videos/Movies | /export/kingdezigns-public/04 - Videos/Movies |
|
|||
|
|
| Photos | /media/02 - Photos | /export/kingdezigns-public/02 - Photos |
|
|||
|
|
|
|||
|
|
### **Plex Pass**
|
|||
|
|
- **Lifetime Plex Pass** — confirmed active on this server
|
|||
|
|
- Enables: Sonic Radio, hardware transcoding (Intel Quick Sync), mobile sync, offline downloads
|
|||
|
|
|
|||
|
|
### **Library Codec Profile**
|
|||
|
|
| Codec | Count | Notes |
|
|||
|
|
|-------|-------|-------|
|
|||
|
|
| HEVC Main 10 | ~90 | 10-bit HDR — Direct Streams on Roku, hardware transcode via Quick Sync if needed |
|
|||
|
|
| H.264 High | 29 | Direct Play on all clients |
|
|||
|
|
| Audio | 119 AAC | 100% AAC — never forces audio transcode |
|
|||
|
|
| Subtitles | 1168 SRT + 279 PGS | PGS only burns in if manually selected — Subtitle Mode set to Manually Selected |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🐳 Docker Configuration
|
|||
|
|
|
|||
|
|
### **Compose File Location**
|
|||
|
|
```
|
|||
|
|
~/docker/plex/docker-compose.yml
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **Compose Contents**
|
|||
|
|
```yaml
|
|||
|
|
services:
|
|||
|
|
plex:
|
|||
|
|
image: lscr.io/linuxserver/plex:latest
|
|||
|
|
container_name: plex
|
|||
|
|
network_mode: host
|
|||
|
|
environment:
|
|||
|
|
- PUID=1000
|
|||
|
|
- PGID=1001
|
|||
|
|
- TZ=America/New_York
|
|||
|
|
- VERSION=docker
|
|||
|
|
devices:
|
|||
|
|
- /dev/dri:/dev/dri
|
|||
|
|
group_add:
|
|||
|
|
- "44"
|
|||
|
|
- "993"
|
|||
|
|
volumes:
|
|||
|
|
- /opt/plex/config:/config
|
|||
|
|
- /opt/plex/transcode:/transcode
|
|||
|
|
- /mnt/plex/media:/media
|
|||
|
|
restart: unless-stopped
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> **devices + group_add:** Required for Intel Quick Sync hardware transcoding. The container needs `/dev/dri` device access plus supplementary group membership for `video` (GID 44) and `render` (GID 993). Without `group_add`, Plex silently falls back to software transcoding even with hardware acceleration enabled in settings.
|
|||
|
|
|
|||
|
|
### **Key Docker Commands**
|
|||
|
|
```bash
|
|||
|
|
# Start all services
|
|||
|
|
cd ~/docker/plex && docker compose up -d
|
|||
|
|
|
|||
|
|
# Stop all services
|
|||
|
|
cd ~/docker/plex && docker compose down
|
|||
|
|
|
|||
|
|
# Update Plex
|
|||
|
|
cd ~/docker/plex && docker compose pull && docker compose up -d
|
|||
|
|
|
|||
|
|
# View Plex logs
|
|||
|
|
docker logs plex --tail 50
|
|||
|
|
|
|||
|
|
# View Tautulli logs
|
|||
|
|
docker logs tautulli --tail 50
|
|||
|
|
|
|||
|
|
# Check container status
|
|||
|
|
docker ps
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎬 Hardware Transcoding — Intel Quick Sync
|
|||
|
|
|
|||
|
|
The J4105 supports Intel Quick Sync via `/dev/dri`. Both decode and encode are offloaded to the GPU.
|
|||
|
|
|
|||
|
|
### **Verification**
|
|||
|
|
Check GPU access from inside the container:
|
|||
|
|
```bash
|
|||
|
|
docker exec -it plex id
|
|||
|
|
# Should show groups 44 (video) and 993 (render/group01b4) in output
|
|||
|
|
docker exec -it plex ls -la /dev/dri
|
|||
|
|
# Should show card0 and renderD128
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Verify during active transcode — Plex Dashboard should show:
|
|||
|
|
```
|
|||
|
|
4K (HEVC Main 10) (hw)
|
|||
|
|
SD (HEVC) — Transcode (hw)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The `(hw)` suffix on both lines confirms Quick Sync is handling decode and encode.
|
|||
|
|
|
|||
|
|
### **Plex Settings**
|
|||
|
|
- Settings → Transcoder → **Use hardware acceleration when available** ✅
|
|||
|
|
- Settings → Transcoder → **Use hardware-accelerated video encoding** ✅
|
|||
|
|
- Settings → Transcoder → **Transcoder temporary directory:** `/transcode`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 Tautulli
|
|||
|
|
|
|||
|
|
Tautulli provides playback history, live session monitoring, and email alerts.
|
|||
|
|
|
|||
|
|
- **Web UI:** `http://192.168.150.45:8181`
|
|||
|
|
- **Config:** `/opt/tautulli/config`
|
|||
|
|
- **Connected to:** Plex at `192.168.150.45:32400`
|
|||
|
|
|
|||
|
|
### **Active Notifications**
|
|||
|
|
| Trigger | Destination | Purpose |
|
|||
|
|
|---------|-------------|---------|
|
|||
|
|
| Plex Server Down | rufus.king@kingdezigns.com | Alert when Plex stops responding |
|
|||
|
|
| Plex Server Back Up | rufus.king@kingdezigns.com | Confirmation when Plex recovers |
|
|||
|
|
|
|||
|
|
### **SMTP Configuration**
|
|||
|
|
Uses same Zoho SMTP credentials as all other KingDezigns email alerts:
|
|||
|
|
- Server: `smtppro.zoho.com` / Port: `465` / TLS enabled
|
|||
|
|
- From: `rufus.king@kingdezigns.com`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📁 NFS Mounts
|
|||
|
|
|
|||
|
|
Configured in `/etc/fstab` — mounts automatically on boot:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
192.168.150.35:/export/kingdezigns-all/Docker/plex/config /mnt/plex/config nfs defaults,_netdev 0 0
|
|||
|
|
192.168.150.35:/export/kingdezigns-all/Docker/plex/transcode /mnt/plex/transcode nfs defaults,_netdev 0 0
|
|||
|
|
192.168.150.35:/export/kingdezigns-public /mnt/plex/media nfs defaults,_netdev 0 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> Note: `/mnt/plex/config` is used by `plex32_backup.sh` as the backup destination — not by the Plex container. `/mnt/plex/transcode` exists in fstab but Plex uses the local `/opt/plex/transcode` directory instead.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🌍 External Access
|
|||
|
|
|
|||
|
|
- **Domain:** `plex.kingdezigns.com`
|
|||
|
|
- **Proxy:** NPM on HAS (192.168.150.30) → 192.168.150.45:32400
|
|||
|
|
- **SSL:** Let's Encrypt via NPM
|
|||
|
|
- **Port:** 32400 (Plex default)
|
|||
|
|
- **Port forwarding:** UCG Max forwards external 32400 → 192.168.150.45
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔒 Required Firewall Behavior
|
|||
|
|
|
|||
|
|
### **Inbound to PLEX32**
|
|||
|
|
- Allowed from VLAN 1 (Infrastructure) and VLAN 20 (Trusted) — global rules
|
|||
|
|
- Allowed from VLAN 50 (local VLAN)
|
|||
|
|
- External Plex traffic arrives via NPM on HAS (192.168.150.30)
|
|||
|
|
|
|||
|
|
### **Outbound from PLEX32**
|
|||
|
|
- Subject to VLAN 50's global final drop
|
|||
|
|
- Cannot initiate to VLANs 1, 10, 20, 30, 40
|
|||
|
|
- Allowed: local VLAN 50 traffic (NFS to NAS08, DNS to Pi-hole, SSH to NAS08 for backup)
|
|||
|
|
|
|||
|
|
### **Critical Requirements**
|
|||
|
|
- Must reach NAS08 (192.168.150.35) on NFS ports for media library and backup destination
|
|||
|
|
- Must be reachable from HAS (192.168.150.30) for proxied external traffic
|
|||
|
|
- Port 32400 must be forwarded on UCG Max for direct Plex remote access
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔐 Security Measures
|
|||
|
|
- **Fail2Ban enabled** — `ignoreip` includes `127.0.0.1/8`, `::1`, `192.168.150.0/24`
|
|||
|
|
- Docker-based service isolation
|
|||
|
|
- VLAN-level containment
|
|||
|
|
- No outbound access to other VLANs
|
|||
|
|
|
|||
|
|
### **Fail2Ban Configuration**
|
|||
|
|
`/etc/fail2ban/jail.local`:
|
|||
|
|
```ini
|
|||
|
|
[DEFAULT]
|
|||
|
|
ignoreip = 127.0.0.1/8 ::1 192.168.150.0/24
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **System Timezone**
|
|||
|
|
- Timezone corrected to `America/New_York` on 2026-06-21 — was previously `Etc/UTC`
|
|||
|
|
- The UTC default caused cron jobs to fire 4 hours early (e.g. 2 AM UTC = 10 PM EDT)
|
|||
|
|
- Always verify timezone after fresh installs: `timedatectl | grep "Time zone"`
|
|||
|
|
- Fix: `sudo timedatectl set-timezone America/New_York`
|
|||
|
|
|
|||
|
|
### **sudo Without Password**
|
|||
|
|
`/etc/sudoers.d/rufusking-nopasswd`:
|
|||
|
|
```
|
|||
|
|
rufusking ALL=(ALL) NOPASSWD: /usr/bin/apt-get update, /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status sshd, /sbin/reboot
|
|||
|
|
```
|
|||
|
|
Required by update check script (`apt-get update`), health report script (`fail2ban-client`), and remote reboot script (`reboot`).
|
|||
|
|
|
|||
|
|
### **SSH Key Authentication**
|
|||
|
|
PLEX32 has an SSH key configured to NAS08 — required for the backup script to fetch accurate disk stats without a password prompt:
|
|||
|
|
```bash
|
|||
|
|
# Key location on PLEX32
|
|||
|
|
~/.ssh/id_ed25519
|
|||
|
|
|
|||
|
|
# Test
|
|||
|
|
ssh rufusking@192.168.150.35 "echo NAS08 OK"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **AC Power Recovery (Auto Power-On After Outage)**
|
|||
|
|
Configured **2026-06-30** after a power outage left PLEX32 powered off (manual power button press required to bring it back up).
|
|||
|
|
|
|||
|
|
Dell exposes AC Power Recovery as a writable SMBIOS token — this means it can be set **remotely from within Linux**, without ever entering BIOS setup.
|
|||
|
|
|
|||
|
|
**Tooling:**
|
|||
|
|
```bash
|
|||
|
|
sudo apt-get install -y libsmbios-bin smbios-utils
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Relevant tokens (mutually exclusive — only one should be `true`):**
|
|||
|
|
| Token | Setting | Desired State |
|
|||
|
|
|-------|---------|----------------|
|
|||
|
|
| `0x00a1` | AC Power Recovery Mode (Off) — stays off | `false` |
|
|||
|
|
| `0x00a2` | AC Power Recovery Mode (Last) — restores prior state | `false` |
|
|||
|
|
| `0x00a3` | AC Power Recovery Mode (On) — powers on automatically | `true` ✅ |
|
|||
|
|
|
|||
|
|
**Command used to set it:**
|
|||
|
|
```bash
|
|||
|
|
sudo smbios-token-ctl --activate --token-id=0x00a3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Verify current state:**
|
|||
|
|
```bash
|
|||
|
|
sudo smbios-token-ctl --dump-tokens | grep -i -A2 "AC Power Recovery"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> Note: `smbios-token-ctl --dump-tokens` throws a harmless `StopIteration` traceback after printing all tokens (hits a malformed token entry near the end of the table) — this does not affect the accuracy of the data already printed, including the AC Power Recovery tokens.
|
|||
|
|
|
|||
|
|
**Result:** PLEX32 will now power back on automatically as soon as AC power is restored after an outage — no physical power button press required. (HAS already had equivalent behavior configured.)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🗂️ Shared Permissions
|
|||
|
|
|
|||
|
|
PLEX32 uses the same `ncshare` group as NAS08 and NAS16 for consistency across the network.
|
|||
|
|
|
|||
|
|
| Item | Value |
|
|||
|
|
|------|-------|
|
|||
|
|
| Group name | `ncshare` |
|
|||
|
|
| GID | `1001` |
|
|||
|
|
| Members | `rufusking` |
|
|||
|
|
|
|||
|
|
Verify with:
|
|||
|
|
```bash
|
|||
|
|
getent group ncshare
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💾 Config Backup
|
|||
|
|
|
|||
|
|
Plex and Tautulli configs are backed up to NAS08 every 3 days via `plex32_backup.sh`.
|
|||
|
|
|
|||
|
|
### **Backup Script**
|
|||
|
|
- **Location:** `/usr/scripts/plex32/plex32_backup.sh`
|
|||
|
|
- **Method:** tar locally to `/tmp/`, then single `cp` to NFS (rsync avoided — too slow over NFS for Plex's thousands of small metadata files)
|
|||
|
|
- **Plex destination (NAS08):** `/export/kingdezigns-all/Docker/plex/config/backups/plex/plex-config-YYYY-MM-DD.tar.gz`
|
|||
|
|
- **Tautulli destination (NAS08):** `/export/kingdezigns-all/Docker/plex/config/backups/tautulli/tautulli-config-YYYY-MM-DD.tar.gz`
|
|||
|
|
- **Retention:** 3 copies of each, older ones auto-deleted
|
|||
|
|
- **Excludes:** Cache, Logs, Crash Reports
|
|||
|
|
- **Email:** HTML report sent on completion — SUCCESS / PARTIAL / FAILED
|
|||
|
|
- **SMTP password:** `/etc/plex32-smtp-pass` (chmod 600, owned by rufusking)
|
|||
|
|
- **Log:** `/var/log/plex32-backup/backup-YYYYMMDD.log` (90 day retention)
|
|||
|
|
|
|||
|
|
### **Restore Procedure (run on plex32)**
|
|||
|
|
```bash
|
|||
|
|
# 1. Stop containers
|
|||
|
|
cd ~/docker/plex && docker compose down
|
|||
|
|
|
|||
|
|
# 2. Copy archive from NAS08
|
|||
|
|
cp /mnt/plex/config/backups/plex/plex-config-YYYY-MM-DD.tar.gz /tmp/
|
|||
|
|
|
|||
|
|
# 3. Clear current config
|
|||
|
|
sudo rm -rf /opt/plex/config/Library
|
|||
|
|
|
|||
|
|
# 4. Extract backup
|
|||
|
|
sudo tar -xzf /tmp/plex-config-YYYY-MM-DD.tar.gz -C /opt/plex
|
|||
|
|
sudo chown -R rufusking:ncshare /opt/plex/config
|
|||
|
|
|
|||
|
|
# 5. Restart containers
|
|||
|
|
cd ~/docker/plex && docker compose up -d
|
|||
|
|
|
|||
|
|
# 6. Verify
|
|||
|
|
docker ps
|
|||
|
|
# Both plex and tautulli should show Up
|
|||
|
|
# Open http://192.168.150.45:32400/web and confirm library and watch history intact
|
|||
|
|
|
|||
|
|
# 7. Cleanup
|
|||
|
|
rm /tmp/plex-config-YYYY-MM-DD.tar.gz
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🖥️ Remote Launcher (KingDezigns001)
|
|||
|
|
|
|||
|
|
A launcher script on KingDezigns001 provides a menu-driven interface to run PLEX32 scripts remotely via SSH.
|
|||
|
|
|
|||
|
|
- **Launcher script:** `~/scripts/plex32_launcher.sh`
|
|||
|
|
- **Desktop shortcut:** `~/Desktop/PLEX32-Launcher.desktop`
|
|||
|
|
- **SSH:** Passwordless — ED25519 key from KingDezigns001 installed on PLEX32
|
|||
|
|
|
|||
|
|
### **Menu Options**
|
|||
|
|
| Option | Script |
|
|||
|
|
|--------|--------|
|
|||
|
|
| 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` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📅 Scheduled Jobs (cron)
|
|||
|
|
|
|||
|
|
| Schedule | Script | Purpose |
|
|||
|
|
|----------|--------|---------|
|
|||
|
|
| Daily at 7:00 AM | `/usr/scripts/plex32/plex32_update_check.sh` | Check for OS, Plex, Docker, Fail2Ban updates — email report |
|
|||
|
|
| Daily at 7:05 AM | `/usr/scripts/plex32/plex32_health_report.sh` | Server health report — disk, NFS, memory, CPU, container status |
|
|||
|
|
| Every 3 days at 2:00 AM | `/usr/scripts/plex32/plex32_backup.sh` | Archive Plex and Tautulli configs to NAS08 — email report |
|
|||
|
|
|
|||
|
|
View cron jobs:
|
|||
|
|
```bash
|
|||
|
|
sudo crontab -l
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> Note: All three PLEX32 scheduled jobs run via **root's crontab**, not `rufusking`'s.
|
|||
|
|
|
|||
|
|
### **plex32_update_check.sh — Docker Pull Fix (2026-07-12)**
|
|||
|
|
The daily update check was intermittently reporting `Plex Media Server = Check failed — could not reach registry` even though `lscr.io` was reachable and disk space was healthy (8.6GB free of 26GB).
|
|||
|
|
|
|||
|
|
**Root cause:** The script's `docker pull` step runs under root's cron environment, which uses a minimal default `PATH` that didn't reliably match the interactive `rufusking` shell environment. The script also silently discarded the actual `docker pull` error output, so the true failure reason was never visible in the log or email — every failure was mislabeled with the same generic "could not reach registry" text regardless of cause.
|
|||
|
|
|
|||
|
|
**Fix applied:**
|
|||
|
|
- Added an explicit `export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` at the top of the script so behavior is consistent under cron regardless of invoking user/environment.
|
|||
|
|
- The script now logs the full `docker pull` output/exit code to `/var/log/plex32-updates/` on failure, and includes it as a red error block in the email report, instead of showing a generic message.
|
|||
|
|
|
|||
|
|
Resolved — manual `sudo` run completed cleanly after the fix, and the failure message no longer appears.
|
|||
|
|
|
|||
|
|
### **plex32_health_report.sh — Issues List Word-Wrapping Fix (2026-07-12)**
|
|||
|
|
The "⚠ Issues Detected" block in the daily health report email was rendering each word of an issue message on its own line (e.g. "WARNING: NAS08 — Plex Config is getting full" appeared as one word per line) instead of as a single readable sentence.
|
|||
|
|
|
|||
|
|
**Root cause:** The script joined multiple issue messages into one string and then split them back apart using `IFS='. ' read -ra ISSUE_LIST`. This does not split on the two-character sequence `". "` as intended — `IFS` treats every character it contains as its own independent delimiter, so both `.` and every space in the text were treated as separate breaks, shattering each sentence into individual words.
|
|||
|
|
|
|||
|
|
**Fix applied:**
|
|||
|
|
- Issue messages are now joined with actual newline characters instead of `". "` when assembled into `ALL_ISSUES`.
|
|||
|
|
- The splitting logic was replaced with a `while IFS= read -r issue; do ... done <<< "$ALL_ISSUES"` loop, which reads one full issue line at a time instead of relying on ambiguous multi-character `IFS` splitting.
|
|||
|
|
|
|||
|
|
Resolved — verified with a manual run; each issue now renders as one complete sentence in the "Issues Detected" section of the email.
|
|||
|
|
|
|||
|
|
### **Email Reports**
|
|||
|
|
| Report | Trigger Colors | Content |
|
|||
|
|
|--------|---------------|---------|
|
|||
|
|
| Update Check | 🟢 Green (current) / 🔵 Blue (updates available) / 🟠 Orange (check failed) | Component versions, available updates, ready-to-run commands |
|
|||
|
|
| Health Report | 🟢 Green (healthy) / 🟠 Orange (warning) / 🔴 Red (critical) | Disk usage, NFS mount status, memory, CPU load, container status, Fail2Ban, reboot flag |
|
|||
|
|
| Config Backup | 🟢 Green (success) / 🟡 Amber (partial) / 🔴 Red (failed) | Archive sizes, durations, copies retained, container status, restore instructions |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧠 Summary for AI Systems
|
|||
|
|
- PLEX32 = **dedicated Plex Media Server + Tautulli** on VLAN 50 at 192.168.150.45.
|
|||
|
|
- Runs on Dell Wyse 5070 (Celeron J4105, 8GB RAM, 32GB eMMC).
|
|||
|
|
- Plex and Tautulli run as Docker containers via `~/docker/plex/docker-compose.yml`.
|
|||
|
|
- **Intel Quick Sync hardware transcoding active** — requires `/dev/dri` device passthrough and `group_add: ["44","993"]` in compose. Without group_add, Plex silently falls back to software transcoding.
|
|||
|
|
- **Lifetime Plex Pass** active — Sonic Radio, hardware transcoding, all Plex Pass features enabled.
|
|||
|
|
- Config and transcode stored **locally** at `/opt/plex/` — not on NFS.
|
|||
|
|
- Media library mounted from NAS08 via NFS at `/mnt/plex/media`.
|
|||
|
|
- `/mnt/plex/config` NFS mount used as backup destination only — not by Plex container.
|
|||
|
|
- Library is ~75% HEVC Main 10, ~25% H.264, 100% AAC audio — audio never transcodes.
|
|||
|
|
- Roku TV (Hisense 55R8BX) Direct Streams video, converts AAC→AC3 for audio (lightweight, expected).
|
|||
|
|
- Subtitle Mode set to Manually Selected — prevents accidental PGS burn-in transcode.
|
|||
|
|
- Publicly accessible via `plex.kingdezigns.com` — proxied through NPM on HAS.
|
|||
|
|
- Port 32400 forwarded on UCG Max for direct Plex remote access.
|
|||
|
|
- Tautulli on port 8181 — Plex down/up email alerts active.
|
|||
|
|
- Fail2Ban active — VLAN 50 subnet whitelisted in ignoreip.
|
|||
|
|
- sudo NOPASSWD configured for apt-get update, fail2ban-client, and reboot — required by automated and remote reboot scripts.
|
|||
|
|
- **System timezone:** `America/New_York (EDT, -0400)` — corrected from `Etc/UTC` on 2026-06-21. UTC default causes cron jobs to fire 4 hours early. Always verify after fresh installs.
|
|||
|
|
- SSH key from PLEX32 → NAS08 configured — required for backup script disk stats.
|
|||
|
|
- **NAS08 root → PLEX32 SSH key configured** — required for remote reboot script (`plex32_reboot.sh` on NAS08).
|
|||
|
|
- SSH key from KingDezigns001 → PLEX32 configured — enables passwordless remote launcher.
|
|||
|
|
- **AC Power Recovery set to "On"** (2026-06-30) via SMBIOS token `0x00a3` using `smbios-token-ctl` — PLEX32 now auto powers-on after AC power is restored following an outage. Configured remotely via SSH, no BIOS access required. Tool: `libsmbios-bin` / `smbios-utils`.
|
|||
|
|
- Config backup runs every 3 days at 2AM — tar + NFS copy, 3 copies retained, HTML email report.
|
|||
|
|
- `ncshare` group (GID 1001) configured for network-wide permission consistency.
|
|||
|
|
- eMMC has no SMART support — health monitored via daily health report email.
|
|||
|
|
- No swap configured — 7.6GB RAM, consistently low utilization.
|
|||
|
|
- Plex on NAS08 is **permanently disabled** (restart: no in compose).
|
|||
|
|
- Migrated from NAS08 on 2026-06-18 — full library, watch history, and playlists preserved.
|
|||
|
|
- **`plex32_update_check.sh` fixed 2026-07-12** — added explicit `PATH` export to fix intermittent `docker pull` failures under root's cron environment; script now logs real `docker pull` error output on failure instead of a generic "could not reach registry" message. All three PLEX32 scheduled jobs run under root's crontab.
|
|||
|
|
- **`plex32_health_report.sh` fixed 2026-07-12** — fixed the "Issues Detected" email block rendering each word on its own line, caused by a multi-character `IFS='. '` split; issues are now newline-delimited and read back with a `while read` loop.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# ✔️ End of File
|