PLEXWEEKLYNAS Portable v1.0.3 walkthrough
Guide0%
QNAP · DOCKER COMPOSE · LINUX

Your Plex newsletter.Native on QNAP.

A richer walkthrough for installing, testing, scheduling, backing up, and safely operating PlexWeekly beside a containerized Tautulli instance—without installing anything inside Tautulli.

plexweekly / ssh verification● ● ●
$ ./plexweekly.sh verify

[OK] Docker daemon available
[OK] Compose command detected
[OK] Tautulli API connected
[OK] Persistent data writable
[OK] Six preview states supported
[OK] Weekly schedule syntax valid

NEXT: ./plexweekly.sh preview-all
No real Plex user email will be sent.

$ |
2equivalent archive formats
6deterministic preview states
1dedicated PlexWeekly service
0credentials included in package
No guide sections matched. Try “Tautulli,” “schedule,” “SMTP,” “backup,” or “preview.”
Before installation

Choose an archive format

ZIP and TAR.GZ contain the same application.

Both files are functionally identical.

Keep both as backups if useful, but install only one into the final QNAP folder. They are two archive formats—not two editions.

ZIP

ZIP archive

Best when extracting on Windows or with a graphical archive utility. After copying to QNAP, mark the shell scripts executable.

Windows-first file
PlexWeekly-NAS-Portable-v1.0.3-QNAP-Docker.zip
TGZ

TAR.GZ archive

Best when extracting directly on QNAP/Linux over SSH. It is the more native Unix format and better preserves executable permissions.

QNAP-first file
PlexWeekly-NAS-Portable-v1.0.3-QNAP-Docker.tar.gz
WorkflowUseExtra step
Extract on Windows, then copy to QNAPZIPchmod +x qnap-install.sh plexweekly.sh
Extract directly on QNAP over SSHTAR.GZPermission repair usually unnecessary, but safe to run
TAR.GZ extraction
mkdir -p /share/Container/plexweekly
tar -xzf PlexWeekly-NAS-Portable-v1.0.3-QNAP-Docker.tar.gz \
  --strip-components=1 \
  -C /share/Container/plexweekly
Deployment model

One dedicated service beside Tautulli

PlexWeekly communicates through APIs and network access.

Separate service

Tautulli upgrades cannot remove PlexWeekly files.

Persistent data

./data keeps configuration, state, logs, and previews.

PS

PowerShell 7

Microsoft’s cross-platform runtime powers the newsletter engine.

Internal scheduler

No Windows Task Scheduler or permanent QNAP cron edits.

QNAP Container Station ├── existing Tautulli container │ └── published host port 8181 │ └── plexweekly container ├── production newsletter engine ├── guarded weekly scheduler ├── ImageMagick artwork processing ├── persistent volume → ./data └── preview web server → NAS port 8787
Keep the services separate.

Installing PlexWeekly inside Tautulli couples upgrades, backups, and troubleshooting. A dedicated container is safer and easier to restore.

Preflight

What the NAS needs

The installer checks most requirements automatically.

  • QNAP Container Station installed and running
  • 64-bit Intel/AMD x86-64 or ARM64 CPU
  • SSH access for the recommended workflow
  • Persistent share such as /share/Container/plexweekly
  • Network access to Tautulli’s published port
  • SMTP provider supporting STARTTLS, normally port 587
  • Tautulli API key and a TestEmail address

Ports used

PortPurpose
8181Tautulli host port
8787LAN preview server
587Typical SMTP STARTTLS
QNAP launch sequence

QNAP install, step by step

Every risky action stays behind preview and TestEmail checkpoints.

1

Install and start Container Station

Confirm Docker is available before beginning.

2

Extract one archive into persistent storage

Recommended folder
/share/Container/plexweekly
3

Connect over SSH

Prepare scripts
cd /share/Container/plexweekly
chmod +x qnap-install.sh plexweekly.sh
4

Run the guided installer

Build, configure, and verify
./qnap-install.sh

The installer creates .env, builds the image, starts the service, runs setup, restarts, and verifies.

5

Review timezone and preview URL

For Arizona, use TZ=America/Phoenix. Confirm PREVIEW_BASE_URL contains the NAS LAN IP.

6

Run safe acceptance testing

No production users yet
./plexweekly.sh verify
./plexweekly.sh list-users
./plexweekly.sh preview-all
./plexweekly.sh send-test-all
./plexweekly.sh schedule-status
7

Enable automatic delivery only after approval

Explicit activation
./plexweekly.sh schedule-enable
Automatic delivery starts disabled.

The container can run, preview, and send to TestEmail without being authorized to email all Plex users.

Unraid deployment

Install as a dedicated Unraid container

The application image is already Linux/Docker compatible. Unraid mainly changes the host paths, ownership defaults, and deployment workflow.

Unraid appdataDocker ComposeContainerized TautulliSix-state testing
Use the same NAS Portable application files.

There is no separate Unraid engine. Deploy the included compose.yaml through Compose Manager or the Docker Compose CLI, and keep PlexWeekly in its own appdata folder.

Skip qnap-install.sh on Unraid.

That wrapper is tailored to QNAP path detection and onboarding. On Unraid, create .env, launch Compose, then use ./plexweekly.sh setup and verify.

1

Create the Unraid appdata project folder

Unraid terminal
mkdir -p /mnt/user/appdata/plexweekly
cd /mnt/user/appdata/plexweekly

Extract either archive here so compose.yaml, Dockerfile, plexweekly.sh, app/, and data/ are all in this directory.

2

Prepare Unraid ownership and environment defaults

Create .env
cp .env.example .env
chmod +x plexweekly.sh
nano .env
Recommended Unraid .env
COMPOSE_PROJECT_NAME=plexweekly
TZ=America/Phoenix
PUID=99
PGID=100
UMASK=077
PREVIEW_BIND=0.0.0.0
PREVIEW_PORT=8787
PREVIEW_BASE_URL=http://UNRAID_SERVER_IP:8787

America/Phoenix is an example. Replace it with the correct timezone and replace UNRAID_SERVER_IP with the LAN address of the Unraid host.

3

Deploy through Compose Manager or the CLI

Compose Manager Point the stack/project at /mnt/user/appdata/plexweekly/compose.yaml, review the environment values, then bring the stack up.

CLI equivalent
docker compose build --pull
docker compose up -d
4

Run the application setup inside the new service

Setup and verification
./plexweekly.sh setup
./plexweekly.sh verify
./plexweekly.sh status
5

Connect PlexWeekly to Tautulli

The simplest route is the Unraid host LAN IP and Tautulli’s published host port:

Recommended URL
"TautulliUrl": "http://UNRAID_SERVER_IP:8181"

When both services share the same custom Docker network, the Tautulli container name can be used instead:

Shared custom network
"TautulliUrl": "http://tautulli:8181"
6

Run the same safe acceptance sequence

No production users yet
./plexweekly.sh list-users
./plexweekly.sh preview-all
./plexweekly.sh send-test-all
./plexweekly.sh schedule-status

Open the preview index at http://UNRAID_SERVER_IP:8787/preview-all/preview-all-00-INDEX.html.

7

Enable the internal weekly scheduler after approval

Explicit activation
./plexweekly.sh schedule-enable
/data

Persistent mapping

The included relative volume ./data:/data resolves to /mnt/user/appdata/plexweekly/data when the Compose project lives in the recommended folder.

8787

Unraid WebUI target

Use http://[IP]:[PORT:8787]/preview-all/preview-all-00-INDEX.html as the container WebUI URL when creating a custom Unraid template later.

No Community Applications XML is included in v1.0.3.

The supported Unraid route for this package is Docker Compose. A future Unraid-specific release could add a one-click CA template without changing the newsletter engine.

API connection

Connect to containerized Tautulli correctly

Avoid the most common container networking mistake.

Do not use http://127.0.0.1:8181.

Inside PlexWeekly, localhost means PlexWeekly—not the QNAP host or the separate Tautulli container.

Recommended: QNAP LAN IP

Use the same host address another LAN device uses to open Tautulli.

config.json
"TautulliUrl": "http://192.168.1.50:8181"

Advanced: shared Docker network

Attach both containers to a user-defined network and resolve Tautulli by service name.

Shared-network example
"TautulliUrl": "http://tautulli:8181"
Direct Plex metadata is optional.

PlexServerUrl and PlexToken improve clearLogo support. Tautulli and text-title fallbacks remain available.

Setup wizard

Configuration that matters

The wizard writes live settings into persistent storage.

Tautulli

URL, API key, history window, watched thresholds, and exclusions.

Email

Sender, Reply-To, SMTP host/port, authentication, and TestEmail.

Branding

Server label, footer name, Plex web link, and optional direct Plex metadata.

Schedule

Day, local time, grace period, polling, and enabled state.

Safety

Ten-second production recipient delay and shorter test delay.

Eligibility

Excluded users/emails and recent-access classification window.

Selected config fields
{
  "TautulliUrl": "http://192.168.1.50:8181",
  "ApiKey": "PASTE_YOUR_TAUTULLI_API_KEY_HERE",
  "FooterServerName": "My Plex",
  "FromName": "My Plex Newsletter",
  "SmtpHost": "smtp.example.com",
  "SmtpPort": 587,
  "SmtpEnableSsl": true,
  "TestEmail": "you@example.com",
  "ScheduleDay": "Friday",
  "ScheduleTime": "09:30",
  "SendDelaySeconds": 10,
  "ScheduleEnabled": false
}
Credentials stay outside the image.

The live config is stored under data/, receives restrictive permissions, and is not included in either distributed archive.

Operational controls

Command center

Every routine action is exposed through ./plexweekly.sh.

CommandWhat it doesRisk
verifyChecks runtime, assets, config, Tautulli, SMTP reachability, and schedule syntax.Safe
list-usersLists selectable Plex/Tautulli users.Safe
preview [user]Creates one browser preview.No email
preview-all [user]Creates six HTML states and an index.No email
send-test [user]Sends one message only to TestEmail.TestEmail
send-test-all [user]Sends all six only to TestEmail.TestEmail
welcome [user]Sends a real welcome and records state.Real recipient
send-allSends to every eligible Plex user after confirmation.Bulk send
rosterDisplays access/welcome state.Safe
repair-assetsRepairs email-safe assets.Safe
logs / statusShows logs or service state.Safe
restart / updateRestarts or rebuilds without deleting data.Service change
backupArchives persistent data.Safe
shellOpens a container shell.Advanced
Visual acceptance

Six-state regression suite

All states use the production renderer and MIME/CID pipeline.

01

Manual welcome

One-off onboarding without weekly stats, quiet copy, or warm-up copy.

WELCOMEONLY = TRUE
02

New user — no history

First scheduled delivery with onboarding replacing empty statistics.

RECENTACCESS + ZERO STATS
03

New user — with history

First scheduled delivery with populated statistics.

RECENTACCESS + ACTIVITY
04

Established normal

Populated established newsletter; sample stats only when needed for layout testing.

NORMAL ACTIVITY
05

Established quiet

Zero activity after warm-up, showing QUIET IN THIS SECTOR.

WARMINGUP = FALSE
06

Established warm-up

Zero activity during the initial window, showing STATS ARE WARMING UP.

WARMINGUP = TRUE
Create all previews
./plexweekly.sh preview-all
Preview index
http://QNAP_LAN_IP:8787/preview-all/preview-all-00-INDEX.html
Test delivery is isolated.

send-test-all sends six real SMTP messages, but every message goes exclusively to TestEmail.

Card presentation

Movie genres and TV overflow counts

The same card renderer is used by browser previews, TestEmail, welcomes, normal newsletters, and quiet-week Latest Releases.

TV

Three visible episode rows

TV cards show the three newest episode rows. Totals above three add a gold footer at the bottom: 1 additional episode recently added for four total, or X additional episodes recently added for five or more. Totals from one through three show no footer.

MOV

Genre metadata under the title

Movie cards show up to two genres directly below the title. More than two becomes Genre One, Genre Two, and more. The 13px / 500-weight line uses the same muted color as the description and disappears entirely when no genre metadata is available.

Spacing stays deterministic.

Movie content height was expanded for the genre row, while TV details reserve room for three episode lines and a bottom-aligned overflow footer.

Automatic delivery

Scheduling with guardrails

The scheduler records attempts to avoid duplicates.

Disabled initially

ScheduleEnabled=false until explicitly enabled.

Timezone-aware

Day and time follow TZ in .env.

Grace window

A short restart after send time can still be handled.

Duplicate guard

The attempt is recorded before delivery starts.

Schedule controls
./plexweekly.sh schedule-status
./plexweekly.sh schedule-enable
./plexweekly.sh schedule-disable
./plexweekly.sh schedule-reset
schedule-reset is deliberately dangerous.

It clears today’s attempt guard. A later scheduler poll may permit another real SendAll on the same day.

Persistence

Data, backups, and updates

The data folder is the installation’s durable identity.

data/ ├── config.json ├── state.json ├── access-state.json ├── scheduler-state.json ├── assets/ ├── output/ └── logs/

State protection

  • config.json: API, SMTP, branding, schedule
  • state.json: warm-up tracking
  • access-state.json: new-user and welcome history
  • scheduler-state.json: same-day send guard
Create a dated backup
./plexweekly.sh backup
Rebuild without deleting data
./plexweekly.sh update
Backups contain credentials.

The backup includes config.json. Treat the generated archive as a private credential-bearing file.

Hardening

Security boundaries

The package minimizes exposure; the administrator controls LAN and credentials.

Built-in boundaries

  • Runtime drops to a configurable non-root UID/GID
  • Root UID/GID values are refused
  • UMASK=077 restricts new files
  • Credentials live in persistent storage, not the image
  • Welcome and SendAll require confirmation

Administrator responsibilities

  • Never port-forward preview port 8787 publicly
  • Do not commit .env or config.json
  • Use STARTTLS; implicit SMTPS 465 is unsupported
  • Keep backups private
  • Review all six TestEmail messages before scheduling
Diagnostics

Common problems

Start with verification and logs.

Tautulli verification fails
Use the QNAP LAN IP and Tautulli’s published port, not 127.0.0.1. Recheck the API key and verify Tautulli opens from another LAN device.
The preview page does not open
Run ./plexweekly.sh status and ./plexweekly.sh logs. Confirm port 8787 and PREVIEW_BASE_URL use the current NAS LAN IP.
Shell scripts say permission denied
Run chmod +x qnap-install.sh plexweekly.sh. This is common after Windows ZIP extraction.
SMTP cannot authenticate
Confirm STARTTLS, normally port 587, username, and app password/provider credentials. Port 465 implicit SMTPS is unsupported.
The service is healthy but no scheduled email arrives
Check schedule-status, ScheduleEnabled, TZ, day/time, grace window, and the same-day attempt guard.
Container Station recreates the container
That is safe while the project folder and data/ bind mount remain intact. The container layer is disposable.
Complete reference

Recommended operating sequence

The shortest safe path from extraction to scheduled delivery.

Full safe workflow
cd /share/Container/plexweekly
chmod +x qnap-install.sh plexweekly.sh

./qnap-install.sh

./plexweekly.sh verify
./plexweekly.sh list-users
./plexweekly.sh preview-all
./plexweekly.sh send-test-all
./plexweekly.sh schedule-status

# Enable only after approving all six TestEmail messages:
./plexweekly.sh schedule-enable

Statically validated

Archive integrity, shell syntax, Compose structure, Linux-safe paths, non-root startup, and six-state wiring.

Environment validation

Your QNAP, Docker daemon, Tautulli, Plex metadata, LAN, SMTP provider, and mail clients still require acceptance testing.

Schedule authorization

Enabling the schedule authorizes confirmation-free weekly SendAll at the configured local time.

Final acceptance gate.

Do not enable the schedule until the browser preview index and all six TestEmail messages are approved on desktop and mobile clients.

v1.0.3 correction

TV overflow counts now survive Tautulli aggregation

When Tautulli reports one show or season row, PlexWeekly inspects child episode timestamps inside the newsletter window and calculates the true remainder beyond the three visible rows.

Display remains capped

Only the three newest episode rows appear on the card.

Footer uses the real total

Four total produces 1 additional episode recently added; five or more uses the exact remaining count.

Reserved footer space

A wrapped title or IMDb row no longer pushes the gold overflow line outside the visible card.

Movie genre formatting is retained.

The muted 13px, weight-500 line stays directly under the movie title with the first two genres and “, and more” when applicable.

v1.0.3 layout correction

Global movie genre placement

Movie genres now appear immediately beneath the title identity on regular cards and every movie-based hero layout.

Regular cards

The title and genre use normal flow, eliminating the large blank gap caused by fixed-height table distribution.

Desktop hero

The genre appears below either the clearLogo or the normal fallback title.

Mobile hero

The genre appears below the normal title while the mobile banner remains clearLogo-free.

One approved style everywhere.

13px, weight 500, muted description color, first two genres plus “, and more,” and no blank row when genre metadata is missing.