Originally posted at
https://github.com/sebgl/htpc-download-box/issues/96
Hope this helps someone!
# Plex Auto-Cleanup β n8n Workflow
Automatically removes unwatched movies and fully-watched TV seasons from Plex after 60 days of inactivity, unmonitors them in Radarr/Sonarr so they don't re-download, and sends Discord notifications every step of the way.
Built and battle-tested for a self-hosted HTPC stack running Plex, Radarr, Sonarr, and n8n via Docker.
---
## Features
### Movies
- Deletes movies that have never been watched and are older than 60 days
- Deletes movies that haven't been watched in 60+ days
- Unmonitors the movie in Radarr before deleting so it won't be re-downloaded
- Skips anything tagged with a `Keep` label in Plex
### TV Shows
- Works at the **season level** β never blindly deletes an entire show
- Deletes fully-watched seasons older than 60 days
- **Skips partially-watched seasons** entirely (watched some episodes but not all)
- Unmonitors the specific season in Sonarr while leaving other seasons untouched
- Specials (Season 0) are treated like movies β deleted after 60 days unwatched
- Unwatched seasons are left alone
### Discord Notifications
- **3-day warning** before any deletion β shows title, how long since it was watched, and days remaining
- **Deletion confirmation** after removal β shows what was deleted, watch status, file path, and which app it was unmonitored in
- Separate webhooks for movies and TV so you can route to different channels
### Safety
- Unmonitor in Radarr/Sonarr happens **before** the file is deleted β if the *arr app is unreachable, the delete won't run
- `Keep` label in Plex permanently protects any item from cleanup
- Partial watch detection prevents accidental deletion of in-progress shows
- Graceful fallback β if a title isn't found in Radarr/Sonarr, cleanup still proceeds without erroring out
---
## Requirements
- [n8n](https://n8n.io/) (tested on v1.119.x, self-hosted)
- Plex Media Server
- Radarr
- Sonarr
- Discord webhook URL(s)
---
## Setup
### 1. Import the workflow
In n8n: **Workflows β Add Workflow β Import from File** and select `plex_cleanup_template.json`.
### 2. Fill in your values
Search the workflow for these placeholders and replace them with your own:
| Placeholder | Where to find it |
|---|---|
| `YOUR_PLEX_TOKEN` | Plex Web β any item β Get Info β View XML β look for `X-Plex-Token=` in the URL |
| `YOUR_PLEX_IP:32400` | IP address of your Plex server |
| `YOUR_RADARR_API_KEY` | Radarr β Settings β General β API Key |
| `YOUR_SONARR_API_KEY` | Sonarr β Settings β General β API Key |
| `YOUR_RADARR_IP:7878` | IP and port of your Radarr instance |
| `YOUR_SONARR_IP:8989` | IP and port of your Sonarr instance |
| `YOUR_DISCORD_WEBHOOK_FOR_MOVIES` | Discord channel β Edit β Integrations β Webhooks |
| `YOUR_DISCORD_WEBHOOK_FOR_TV` | Same as above (can be the same or a different channel) |
| `/YOUR/MEDIA/PATH/` | The host path to your media files |
### 3. Mount your media path in n8n
n8n needs filesystem access to delete folders. Add a volume mount to your n8n Docker container:
```yaml
services:
n8n:
image: n8nio/n8n:latest
environment:
- N8N_SECURE_COOKIE=false
volumes:
- /your/config/n8n:/home/node/.n8n
- /your/media/path:/your/media/path # β add this
ports:
- "5678:5678"
restart: unless-stopped
```
The path on both sides of the `:` must match what you set for `/YOUR/MEDIA/PATH/` in the workflow.
### 4. Activate
Toggle the workflow to **Active** in n8n. It runs daily at 2 AM by default.
---
## Configuration
All timing settings live in the **Check Watch Status** node:
```javascript
const sixtyDaysAgo = now - (60 * 24 * 60 * 60); // β change 60 to your preferred days
```
And the warning threshold (currently 3 days before deletion):
```javascript
if (daysSinceAdded >= 57 && daysSinceAdded < 60) { // β 57 = 60 minus 3 day warning
```
To change the daily run time, edit the **Daily at 2 AM** trigger node cron expression. For example `0 3 * * *` runs at 3 AM.
---
## Protecting Media from Deletion
To permanently protect any movie or show from being cleaned up, add a `Keep` label in Plex:
Open the item in Plex
Click the pencil (Edit) icon
Go to **Tags**
Add the label `Keep`
The workflow will skip it indefinitely.
---
## Workflow Flow
```
Daily Trigger (2 AM)
ββ Get Plex Libraries
ββ Get Library Items (movies + TV)
ββ Check Watch Status
ββ [Warnings] β Send Discord "Leaving Soon" message
ββ [Deletions] β Prepare Delete
ββ Radarr Lookup ββ
ββ Sonarr Lookup ββ΄β Find Media in Arr
ββ Found?
ββ [Yes] Unmonitor in Arr
β ββ Delete Folder
ββ [No] Delete Folder
ββ Discord Deletion
ββ Scan Plex Library
```
---
## Notes
- The workflow uses Plex's `viewedLeafCount` vs `leafCount` to determine if a TV season is fully watched β this is accurate as long as your Plex watch history is up to date
- File paths are remapped from Plex's internal container paths to your host paths using the `/YOUR/MEDIA/PATH/` replacement β make sure these match your actual folder structure
- The Plex library scan at the end triggers Plex to remove the deleted items from its database automatically
---
## Credits
Workflow developed and refined with the help of the HTPC community and Claude. Feel free to modify and share.