Self-hosted Plex request and availability hub for Sonarr, Radarr, downloads, VO/VF tracking and grou
10K+
Watchdeck turns "someone requested a movie" into "it's playing in the right language" — without a spreadsheet.
It receives requests from Plex watchlists, RSS, its own API, Seerr, or its responsive web interface, follows the download through to import, confirms the title is actually in Plex, tracks VO/VF (dub) coverage per season/episode, and sends grouped notifications through email, Discord, Telegram, ntfy or Gotify.
Those tools are great at the request front door and largely stop once Sonarr/Radarr picks the request up. Watchdeck stays involved for the whole trip: stuck-import detection, confirmed Plex availability, and per-episode audio-track (VF/VO) tracking before anyone gets notified. It has its own request intake (UI/API/watchlist), so Seerr integration is optional, not required.
It also supports Plex's Universal Watchlist RSS feed (Plex Pass): one admin-configured URL aggregates every friend's watchlist, so nobody has to sign into Watchdeck or generate a token just to get their requests picked up — unlike Overseerr/Jellyseerr, which need each user to authenticate at least once.
Python 3.12 / FastAPI / SQLAlchemy 2 (async) / Alembic on the backend, Vue 3 + Vite on the frontend, ARQ over Redis for background jobs, PostgreSQL 15 as the system of record, Server-Sent Events (backed by Redis Streams) for real-time UI updates, and Fernet-encrypted secrets at rest. Same image runs both the API and the worker — only the container command changes.
Plex watchlist / API / Seerr / UI
|
v
Watchdeck API + Vue UI ---- PostgreSQL
|
Redis
|
ARQ worker
/ | \
Plex Sonarr/Radarr Notifications
|
Download clients
The same image is used for both the web/API service and the ARQ worker. A production deployment requires:
mrcryllix/watchdeck:latest for the API;mrcryllix/watchdeck:latest with the ARQ command for the worker;| Tag | Meaning |
|---|---|
latest | Last successful build from main. Moves on every merge — fine for personal instances, riskier for production since a bad merge ships immediately. |
vX.Y.Z | Built from a Git tag (git tag vX.Y.Z), immutable. Pin to one of these for a production deployment so an update is a deliberate docker compose pull after you've read the changelog, not an automatic drift. |
Images are published to both Docker Hub (mrcryllix/watchdeck) and GitHub Container Registry (ghcr.io/remi-deher/watchdeck) for the same tags. Only linux/amd64 is built at the moment — no arm64 image yet.
The full, up-to-date Compose file — including the backup/restore profile, environment variables and volume layout — lives in the GitHub README. Rather than duplicate it here (and risk it drifting out of sync), the short version below covers just enough to get running; follow the README link for anything beyond the basics.
Create .env:
TZ=Europe/Paris
POSTGRES_DB=watchdeck
POSTGRES_PASSWORD=replace-with-a-long-random-password
WATCHDECK_ENCRYPTION_KEY=replace-with-a-fernet-key
ARQ_MAX_JOBS=4
ARQ_JOB_TIMEOUT=3600
Generate the encryption key with:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Grab docker-compose.yml from the repo (it targets the published image out of the box once you swap build: . for image: mrcryllix/watchdeck:latest, or pin it to a vX.Y.Z tag per the table above):
curl -O https://raw.githubusercontent.com/remi-deher/watchdeck/main/docker-compose.yml
Start the stack:
docker compose up -d
docker compose ps
Open http://localhost:8000 and follow the first-run wizard.
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 api worker
Database migrations run when the API container starts.
pgdata.redisdata../data.WATCHDECK_ENCRYPTION_KEY in .env.Back up both PostgreSQL and the encryption key. Losing the encryption key prevents Watchdeck from decrypting stored integration secrets.
docker compose exec worker arq --check app.jobs.WorkerSettings
docker compose exec redis redis-cli ping
docker compose exec db pg_isready -U watchdeck -d watchdeck
GET /api/healthGET /api/metrics/prometheusapi stuck "unhealthy" after an update, worker never starts: almost always a failed Alembic migration on container start. Check docker compose logs api for the migration error before anything else — the worker's depends_on: service_healthy means it won't even attempt to start while the API container is unhealthy.DuplicateTable on a retry: a previous start was interrupted mid-migration, leaving a partially-applied schema change without the migration being marked complete in alembic_version. See the full GitHub README troubleshooting section for the recovery steps.ENABLE_ARQ=1 on both services and that redis-cli ping succeeds — a worker container with no queue connection reports healthy on its own check but silently drops jobs.Full documentation (English and French), backup/restore commands, migration instructions and development setup are available on GitHub.
Watchdeck transforme « quelqu'un a demandé un film » en « il tourne dans la bonne langue », sans tableur. Il récupère les demandes depuis les watchlists Plex, l'API, Seerr ou son interface, les transmet à Sonarr/Radarr, surveille les téléchargements et imports, confirme la présence dans Plex, analyse la couverture VO/VF par saison/épisode et regroupe les notifications.
Le déploiement complet nécessite l'API, un worker ARQ utilisant la même image, PostgreSQL et Redis. Utilisez le fichier Compose ci-dessus, ouvrez http://localhost:8000, puis suivez l'assistant de première configuration.
Consultez le README GitHub pour la documentation complète en français, les sauvegardes et les restaurations.
Content type
Image
Digest
sha256:762b4ea73…
Size
85.4 MB
Last updated
about 15 hours ago
docker pull mrcryllix/watchdeck