Run TautWeekly for Plex
natively on your NAS.
A focused walkthrough for deploying the NAS Portable package with Docker Compose, connecting it to Tautulli, testing every email state, and enabling weekly delivery without modifying the Tautulli container.
What Docker Compose creates
TautWeekly for Plex remains independent from Tautulli while communicating through its HTTP API.
Tautulli
Existing native or containerized service, normally exposed on port 8181.
TautWeekly for Plex container
PowerShell 7, ImageMagick, internal scheduler, email renderer, and preview server.
Persistent data/
Configuration, state, assets, logs, previews, and the automatic-send guard.
./data:/data, uses restart: unless-stopped, and checks both the preview server and a served animated asset.Requirements
Confirm these before extracting the package.
Docker + Compose
docker compose version or the older docker-compose command must work.
64-bit host
Supported host architectures are x86_64/amd64 and aarch64/arm64.
Persistent folder
Use a NAS share that survives container recreation and is included in backups.
Tautulli API
Have the Tautulli URL, published port, and API key available.
SMTP
Use an authenticated STARTTLS provider, typically on port 587.
Test recipient
Choose a private TestEmail before any real Plex user delivery is enabled.
Extract one archive
The ZIP and TAR.GZ contain the same application. Install only one.
Graphical or Windows-first extraction
Use the ZIP when extracting with File Explorer or another graphical archive utility, then copy the folder to your NAS.
TautWeekly-nas-docker.zip
Linux/NAS extraction
Use TAR.GZ when extracting directly on the NAS over SSH.
mkdir -p /srv/containers/tautweekly tar -xzf TautWeekly-nas-docker.tar.gz \ --strip-components=1 \ -C /srv/containers/tautweekly
The folder containing compose.yaml should itself be persistent. Compose uses the relative mount ./data:/data.
Create and edit .env
These host-specific values control permissions, timezone, and the preview address.
Create the file
cd /srv/containers/tautweekly cp .env.example .env
Find a non-root user and group
id -u id -g
Do not configure the container to run as UID or GID 0.
Edit the values
COMPOSE_PROJECT_NAME=tautweekly TZ=America/Phoenix PUID=1000 PGID=1000 UMASK=077 PREVIEW_BIND=0.0.0.0 PREVIEW_PORT=8787 PREVIEW_BASE_URL=http://media.example.test:8787
| Variable | Meaning | Recommended NAS value |
|---|---|---|
TZ | Container and scheduler timezone | IANA zone such as America/Phoenix |
PUID/PGID | Ownership used inside the container | Non-root IDs that can write the project data/ folder |
UMASK | Permissions for new files | 077 |
PREVIEW_BIND | Host interface for the preview port | 0.0.0.0 for LAN access or 127.0.0.1 for host-only |
PREVIEW_PORT | Host preview port | 8787 |
PREVIEW_BASE_URL | URL shown after preview generation | http://NAS_LAN_IP:8787 |
Pull, start, and configure
The image is built locally from the included Dockerfile.
Pull the published image
docker compose pull
Start the service
docker compose up -d docker compose ps
Run the interactive setup wizard
chmod +x tautweekly.sh ./tautweekly.sh setup
After entering the Tautulli URL and API key, select numbered users or ranges to exclude from scheduled and SendAll delivery.
The Plex URL and token are optional. Pressing Enter at the token prompt safely leaves it blank.
Restart after configuration
docker compose restart tautweekly
On QNAP, ./qnap-install.sh creates .env, pulls the published image, starts, runs setup, restarts, and verifies. Container Station can also create an application from compose.qnap.yaml.
Point TautWeekly for Plex at Tautulli
Choose the address according to where Tautulli runs.
Published NAS port
Use the NAS LAN address and Tautulli’s published host port.
http://media.example.test:8181
Shared Docker network
When both services share one custom network, resolve Tautulli by service name.
http://tautulli:8181
Optional direct Plex metadata
Direct Plex URL and token improve clearLogo support. They are not required for normal Tautulli-based operation.
http://media.example.test:32400
127.0.0.1:8181 for another container.Inside TautWeekly for Plex, localhost points back to the TautWeekly for Plex container—not the NAS host and not Tautulli.
Run the acceptance sequence
Keep automatic delivery disabled until these checks pass.
Verify the installation
./tautweekly.sh verify
Checks configuration, Tautulli, assets, SMTP reachability, schedule syntax, and an actual browser-served GIF.
Inspect users and confirm exclusions
./tautweekly.sh list-users ./tautweekly.sh exclude-users
The exclusion command updates only stable user IDs. Press Enter to keep the list or type none to clear it.
Generate all six previews
./tautweekly.sh preview-all
This creates manual welcome, both new-user branches, normal, quiet, and warm-up previews.
Open the preview index
http://NAS_LAN_IP:8787/preview-all-00-INDEX.html
Send all six to TestEmail
./tautweekly.sh send-test-all
No Plex user receives these six messages.
Manual welcome
One-off onboarding without weekly statistics.
New-user branches
First scheduled newsletter with and without viewing history.
Established branches
Normal activity, quiet/no activity, and initial warm-up.
Enable automatic delivery
The scheduler runs inside the container and follows the configured timezone.
Disabled initially
Starting the container does not automatically authorize real sends.
Timezone-aware
The schedule follows TZ from .env.
Same-day guard
The attempt is recorded before sending to reduce duplicate delivery after restarts.
Recipient delay
Production SendAll retains the configured delay between individual recipients.
./tautweekly.sh schedule-status ./tautweekly.sh schedule-enable # Disable later: ./tautweekly.sh schedule-disable
schedule-reset cautiously.It clears today’s automatic-attempt guard. A later scheduler poll may allow another real SendAll on the same day.
Routine Docker Compose controls
Use the wrapper for TautWeekly for Plex actions and Compose itself for container lifecycle operations.
| Task | Command | Effect |
|---|---|---|
| Status | docker compose ps | Shows container state and health. |
| Logs | ./tautweekly.sh logs | Follows the last 200 container log lines. |
| Restart | ./tautweekly.sh restart | Restarts only the TautWeekly for Plex service. |
| Stop | docker compose down | Removes the disposable container/network but preserves data/. |
| Start | docker compose up -d | Recreates the service from the current image and configuration. |
| Shell | ./tautweekly.sh shell | Opens a Bash shell inside the container for diagnostics. |
| Repair assets | ./tautweekly.sh repair-assets | Refreshes email assets and the browser-preview asset mirror. |
Back up and update safely
The container is disposable; the project folder and data/ are the durable installation.
config.jsoncontains Tautulli, SMTP, branding, schedule, and exclusions.state.jsontracks first-run and warm-up state.access-state.jsontracks new users and welcome delivery.scheduler-state.jsonprotects against repeated automatic attempts.
Create a dated backup
./tautweekly.sh backup
The backup includes credentials because it contains config.json. Store it privately.
Pull a published update
docker compose pull docker compose up -d ./tautweekly.sh repair-assets ./tautweekly.sh verify
Do not delete the existing data/ folder during an update.
QNAP, Unraid, and generic Linux
The Compose service is the same; only host paths and identity defaults differ.
Search for TautWeekly for Plex, install the official template, then open the container Console and run Setup-First.ps1 followed by Verify-Setup.ps1. Manual Compose remains available for custom stacks.
| Platform | Suggested project folder | PUID / PGID guidance | Notes |
|---|---|---|---|
| QNAP | /share/Container/tautweekly | Use the IDs returned by id -u and id -g; avoid root. | Container Station with Docker Compose. qnap-install.sh is available. |
| Unraid | /mnt/user/appdata/tautweekly | Template defaults are 99 and 100. | Install from Apps; use Compose Manager only for a custom stack. |
| Generic Linux NAS | /srv/containers/tautweekly or /opt/tautweekly | Use a dedicated non-root service account with write access. | Use the manual Compose workflow in this guide. |
Common problems
Start with status, logs, and verification before changing configuration blindly.
Container is unhealthy
docker compose ps and ./tautweekly.sh logs. The healthcheck requests the preview root and /assets/movies.gif. A failed asset request can make the service unhealthy even when the process is still running.Preview page loads but GIFs or icons are missing
./tautweekly.sh repair-assets and ./tautweekly.sh verify. The current release maintains a real /data/output/assets mirror inside the preview web root; no manual symlink is required.Verification says an animated GIF is unreadable
Tautulli connection fails
NAS_IP:8181 for a published port or tautulli:8181 on a shared Docker network. Do not use 127.0.0.1.Permission denied under data/
PUID and PGID are non-root IDs with write access to the host project folder. Repair ownership on the host, then recreate the container with docker compose up -d.Port 8787 is already in use
PREVIEW_PORT in .env, update PREVIEW_BASE_URL to the same port, and recreate the service.SMTP authentication fails
tautweekly.sh command map
Risk labels distinguish local inspection, TestEmail delivery, real recipients, and scheduler changes.
| Command | Risk | Purpose |
|---|---|---|
verify | SAFE | Runs complete setup and asset verification. |
list-users | SAFE | Lists users available through Tautulli. |
exclude-users | CONFIG | Updates stable recipient exclusions without rerunning setup. |
preview [user] | SAFE | Creates one local/browser preview. |
preview-all [user] | SAFE | Creates all six deterministic HTML previews. |
send-test [user] | TESTEMAIL | Sends one real MIME/CID message only to TestEmail. |
send-test-all [user] | TESTEMAIL | Sends all six states only to TestEmail. |
welcome [user] | REAL SEND | Sends a one-off welcome to the selected Plex user and records state. |
send-all | REAL SEND | Sends one newsletter to every eligible user after confirmation. |
roster | SAFE | Displays first-seen and welcome-state tracking. |
repair-assets | SAFE | Refreshes source assets and the browser-preview mirror. |
schedule-status | SAFE | Shows configured schedule and current guard state. |
schedule-enable | SYSTEM | Authorizes automatic weekly SendAll. |
schedule-disable | SYSTEM | Stops future automatic sends. |
schedule-reset | REAL RISK | Clears today’s automatic-attempt guard. |
backup | SAFE | Creates a dated TAR.GZ of the host data/ directory. |
update | SYSTEM | Pulls the current published image and recreates the service. |
logs | SAFE | Follows recent service logs. |
status | SAFE | Displays Compose service status. |
restart | SYSTEM | Restarts only the TautWeekly for Plex container. |
shell | ADVANCED | Opens a Bash shell inside the container. |
verify → list-users → exclude-users → preview-all → send-test-all → schedule-status → schedule-enable.
Adaptive stats and the Binge Champion award
Light viewing weeks reveal more detail without making heavy viewing weeks excessively tall.
Itemized movies
Mini poster, movie title, Rotten Tomatoes critic score, and audience score.
Itemized episodes
Mini show poster, show title, Sxx EPxx episode title, and IMDb score.
Compact counts
At four or more, the card returns to the large-number format to protect email length.
The user with the most qualifying watch time wins; total plays break an exact-time tie. Every scheduled weekly email shows the same anonymous movie-play, TV-play, and total-time aggregate. Only the winner’s card receives the gold border, larger trophy, and “YOU WON” treatment.
Trending ranks a media title and displays its server-wide play count once. Binge Champion ranks a person by qualifying watch time, but never reveals that person’s name, user ID, or watched titles.
Genre now appears in watched-movie statistics
The compact 1–3 movie recap now matches the rest of the newsletter’s movie hierarchy.
Adaptive movie rows
Each row includes a mini poster, title, genre, and Rotten Tomatoes critic and audience scores.
One genre rule
Show the first two genres and append “, and more” when additional genres exist.
No empty genre row
When metadata has no genre, the rating line moves up automatically with no blank space.
This includes production and portable previews, individual tests, six-state test suites, scheduled weekly newsletters, and all applicable recipient states.
Single-item adaptive cards now retain array semantics
Exactly one watched movie or one streamed episode no longer becomes a scalar under strict mode.
MovieItems and EpisodeItems are initialized and assigned explicitly as arrays before the adaptive renderer checks their counts.
Empty collection
Retains a valid zero-length array.
Single item
Remains an array instead of being unwrapped into a scalar.
Multiple items
Continues to render the adaptive poster rows normally.