All apps · 0 apps
PlexLibrarian
Docker app from UnraidUser-0614219466's Repository
Overview
Readme
View on GitHubplex-librarian
Library health insights for your Plex server — find stale content, track viewing patterns, and reclaim disk space.
Unraid / Docker
Unraid (Community Applications)
Add the template repository URL in Settings → Docker → Template Repositories:
https://raw.githubusercontent.com/BrycePearce/plex-librarian/main/
Then install PlexLibrarian from the Apps tab. Map /data to a persistent path (e.g. /mnt/user/appdata/plex-librarian) and open the web UI to complete setup.
Docker Compose
services:
plex-librarian:
image: ghcr.io/BrycePearce/plex-librarian:latest
container_name: plex-librarian
ports:
- "8288:8080"
volumes:
- /path/to/appdata:/data
environment:
# Optional: skip the OAuth wizard by providing credentials directly
# PLEX_URL: http://192.168.1.100:32400
# PLEX_TOKEN: your-plex-token
restart: unless-stopped
Environment variables
| Variable | Required | Description |
|---|---|---|
DB_PATH |
No | Path to SQLite database (default: /data/librarian.db) |
PORT |
No | HTTP port (default: 8080) |
PLEX_URL |
No | Skip OAuth — direct URL to your Plex server |
PLEX_TOKEN |
No | Skip OAuth — your Plex auth token |
PLEX_WEBHOOK_SECRET |
No | Validates incoming Plex webhook requests |
LIBRARY_SYNC_CONCURRENCY |
No | Max libraries synced in parallel (default: 3) |
FETCH_CONCURRENCY |
No | Max concurrent Plex page requests per library (default: 8) |
SYNC_STALL_TIMEOUT_MINUTES |
No | Abort a sync if it reports no progress for this long, e.g. a Plex host going offline mid-sync (default: 15) |
LOG_RETENTION_DAYS |
No | Days to keep sync history and activity feed entries before they're automatically deleted (default: 180) |
Migrations run automatically on startup. No manual database setup required.
Sync concurrency defaults are deliberately conservative to keep the container's footprint small, especially since Plex itself often runs on the same host. Only raise these if you're running on dedicated hardware with room to spare.
Manual Plex setup (skip OAuth)
Set both PLEX_URL and PLEX_TOKEN to bypass the setup wizard entirely.
Finding your token: In Plex Web, play any item, then click the three-dot menu → "Get Info" → "View XML". Your token is the X-Plex-Token query parameter in the URL. Alternatively, browse to http://<your-plex-host>:32400/identity?X-Plex-Token=<token> — if you get a valid response, the token works.
Finding your server URL: Use http://<local-ip>:32400 for a local server. plex.tv relay URLs (https://xxx.plex.direct:...) also work but local is faster.
Webhooks (Plex Pass only)
Webhooks enable real-time lastViewedAt updates when someone plays or finishes a title, without waiting for a manual sync.
In Plex Web, go to Settings → Webhooks → Add Webhook and enter:
http://<your-host>:8288/api/webhook/plex
If you set PLEX_WEBHOOK_SECRET, append ?token=<secret> to the URL. Requests without a matching token are rejected with 401.
| Event | Effect |
|---|---|
media.play |
Updates lastViewedAt to now |
media.scrobble |
Updates lastViewedAt + viewCount (Plex's 90%-watched threshold) |
API
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /api/settings |
Global defaults, e.g. { staleMinAgeDays } |
| PATCH | /api/settings |
Update global defaults |
| POST | /api/auth/plex/pin |
Start OAuth PIN flow, returns { pinId, authUrl } |
| GET | /api/auth/plex/pin/:id |
Poll PIN status, returns { status, servers? } |
| POST | /api/auth/plex/server |
Save chosen server after OAuth |
| GET | /api/auth/status |
Returns { configured, source, reachable? } — configured: false redirects the UI to setup |
| DELETE | /api/auth/plex |
Disconnect / switch servers |
| GET | /api/libraries |
All synced libraries |
| GET | /api/libraries/:key/stale |
Stale items (supports days, filter, sort, limit, offset, etc.) |
| PATCH | /api/libraries/:key |
Set a per-library staleMinAgeDays override (null to use the global default) |
| GET | /api/libraries/:key/shows/:ratingKey |
Show detail with per-season rollups |
| DELETE | /api/libraries/:key/items |
Delete items' media from Plex (permanent) |
| GET | /api/proxy/thumb |
Server-side Plex thumbnail proxy |
| POST | /api/sync |
Trigger a full sync from Plex |
| POST | /api/sync/libraries/:key |
Trigger a sync for a single library |
| GET | /api/sync/history |
Last 20 sync runs |
| GET | /api/sync/:id |
Status of a specific sync run |
| GET | /api/sync/:id/events |
SSE stream of live sync progress |
| POST | /api/webhook/plex |
Plex webhook receiver (Plex Pass only) |
Development
Prerequisites: Deno 2.x
# Copy and fill in env vars
cp backend/.env.example backend/.env
# Start backend + frontend dev servers
deno task dev
Backend: http://localhost:8080 · Frontend: http://localhost:5173
Database
Migrations run automatically when the server starts. To generate a new migration after schema changes:
cd backend && deno task db:generate
Other backend tasks (run from backend/):
deno task fmt # format
deno task lint # lint
Install PlexLibrarian on Unraid in a few clicks.
Find PlexLibrarian in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/brycepearce/plex-librarian:latestRuntime arguments
- Web UI
http://[IP]:[PORT:8080]/- Network
bridge- Shell
sh- Privileged
- false
- Extra Params
--init
Template configuration
Port for the Plex Librarian web UI.
- Target
- 8080
- Default
- 8288
- Value
- 8288
Directory where the SQLite database is stored. Map this to a persistent location on your Unraid array.
- Target
- /data
- Default
- /mnt/user/appdata/plex-librarian
- Value
- /mnt/user/appdata/plex-librarian
Optional. URL of your Plex Media Server (e.g. http://192.168.1.100:32400). Set this together with Plex Token to bypass the OAuth setup wizard.
- Target
- PLEX_URL
Optional. Your Plex authentication token. Set this together with Plex URL to bypass the OAuth setup wizard.
- Target
- PLEX_TOKEN
Optional. Appended to your Plex webhook URL as ?token=SECRET to authenticate incoming events. Requires Plex Pass.
- Target
- PLEX_WEBHOOK_SECRET
Full path inside the container where the SQLite database file lives. Only change this if you have a specific reason to.
- Target
- DB_PATH
- Default
- /data/librarian.db
- Value
- /data/librarian.db
Max number of libraries synced in parallel. Kept low by default to avoid competing with Plex for resources on the same host — only raise this on dedicated hardware.
- Target
- LIBRARY_SYNC_CONCURRENCY
- Default
- 3
- Value
- 3
Max concurrent Plex page requests per library. Kept low by default to avoid competing with Plex for resources on the same host — only raise this on dedicated hardware.
- Target
- FETCH_CONCURRENCY
- Default
- 8
- Value
- 8
Abort a sync if it reports no progress for this many minutes — e.g. the Plex server going offline mid-sync. Only raise this if you have a very slow or high-latency Plex connection triggering false positives.
- Target
- SYNC_STALL_TIMEOUT_MINUTES
- Default
- 15
- Value
- 15
Days to keep sync history and activity feed entries before they're automatically deleted. Raise this to retain history longer, or set to 0 to keep them forever.
- Target
- LOG_RETENTION_DAYS
- Default
- 180
- Value
- 180