feat(stack): add unified media stack with Sonarr, Radarr, Prowlarr, SABnzbd

Consolidates Jellyfin into a new media stack with full *arr automation:
- Sonarr (8989) - TV show management
- Radarr (7878) - Movie management
- Prowlarr (9696) - Indexer management
- SABnzbd (8880) - Usenet download client
- Jellyfin (8096) - Media streaming with GPU transcoding

Database: All *arr apps use PostgreSQL (postgres-shared) with media_user.
Databases created: sonarr, radarr, prowlarr

Also adds pg-connections.sh utility script for monitoring PostgreSQL connections.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-06 12:39:27 +01:00
co-authored by Claude Opus 4.5
parent dc1fb868fd
commit 6052f81ec1
4 changed files with 396 additions and 85 deletions
-75
View File
@@ -1,75 +0,0 @@
version: '3.8'
# Jellyfin - Media Server with GPU Transcoding
# Backlog: Application Deployment
# Ports: 8096 (HTTP), 8920 (HTTPS), 7359 (Auto-discovery), 1900 (DLNA)
# GPU: YES - Requires NVIDIA Container Toolkit
# Storage: SSD (config/cache), HDD (media files)
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
user: 1000:1000 # Replace with your UID:GID (run: id)
network_mode: host
restart: unless-stopped
volumes:
# Config and cache on SSD (performance-critical)
- /home/jpmschweitzer/docker-data/jellyfin/config:/config
- /home/jpmschweitzer/docker-data/jellyfin/cache:/cache
# Media files on HDD (read-only for safety)
- /mnt/media/jellyfin/movies:/media/movies:ro
- /mnt/media/jellyfin/series:/media/series:ro
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=all
- TZ=Europe/Amsterdam
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:8096/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu, video, compute, utility]
# GPU Transcoding Setup:
# 1. Deploy stack
# 2. Verify GPU access: docker exec jellyfin nvidia-smi
# 3. Access http://localhost:8096
# 4. Complete initial setup wizard
# 5. Navigate to: Dashboard → Playback → Transcoding
# 6. Configure hardware acceleration:
# - Hardware acceleration: NVIDIA NVENC
# - Enable hardware decoding: Check all applicable formats
# - Enable hardware encoding: Enabled
# - Encoding preset: Auto or High Quality
# 7. Test with video playback
# 8. Monitor GPU: watch -n 1 nvidia-smi
#
# Expected Results:
# - Dashboard shows "(hw)" during transcoding
# - nvidia-smi shows Video Engine usage
# - CPU usage remains low during transcoding
# - RTX 2080 Ti can handle multiple 4K transcodes simultaneously
#
# Media Organization:
# /mnt/media/jellyfin/
# ├── movies/
# │ ├── Movie Title (Year)/
# │ │ └── Movie Title (Year).mkv
# ├── tv/
# │ ├── TV Show Name/
# │ │ ├── Season 01/
# │ │ │ ├── S01E01.mkv
# │ │ │ └── S01E02.mkv
# └── music/
# ├── Artist/
# │ ├── Album/
# │ │ └── Track.mp3
+260
View File
@@ -0,0 +1,260 @@
version: '3.8'
# Media Stack - Unified Media Management & Streaming
# Services: Jellyfin, Sonarr, Radarr, Prowlarr, SABnzbd
# Ports: 8096 (Jellyfin), 8989 (Sonarr), 7878 (Radarr), 9696 (Prowlarr), 8880 (SABnzbd)
# GPU: YES (Jellyfin only) - Requires NVIDIA Container Toolkit
# Storage: SSD (configs), HDD (media, downloads)
# Database: PostgreSQL (postgres-shared) for Sonarr, Radarr, Prowlarr
services:
# ============================================================
# JELLYFIN - Media Server with GPU Transcoding
# ============================================================
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
user: 1000:1000
restart: unless-stopped
ports:
- "8096:8096" # HTTP web interface
- "8920:8920" # HTTPS web interface
- "7359:7359/udp" # Auto-discovery
- "1900:1900/udp" # DLNA
volumes:
# Config and cache on SSD (performance-critical)
- /home/jpmschweitzer/docker-data/jellyfin/config:/config
- /home/jpmschweitzer/docker-data/jellyfin/cache:/cache
# Media files on HDD (read-only for safety)
- /mnt/media/jellyfin/movies:/media/movies:ro
- /mnt/media/jellyfin/series:/media/series:ro
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=all
- TZ=Europe/Amsterdam
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:8096/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu, video, compute, utility]
networks:
- docker-dataplane
# ============================================================
# SONARR - TV Show Management & Automation
# ============================================================
sonarr:
image: linuxserver/sonarr:latest
container_name: sonarr
restart: unless-stopped
ports:
- "8989:8989"
volumes:
# Config on SSD
- /home/jpmschweitzer/docker-data/sonarr:/config
# Media and downloads on HDD
- /mnt/media/jellyfin/series:/tv
- /mnt/media/downloads:/downloads
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
# PostgreSQL (postgres-shared)
- SONARR__POSTGRES__HOST=postgres-shared
- SONARR__POSTGRES__PORT=5432
- SONARR__POSTGRES__USER=media_user
- SONARR__POSTGRES__PASSWORD=${MEDIA_DB_PASSWORD}
- SONARR__POSTGRES__MAINDB=sonarr
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:8989/ping || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
depends_on:
- sabnzbd
- prowlarr
networks:
- docker-dataplane
# ============================================================
# RADARR - Movie Management & Automation
# ============================================================
radarr:
image: linuxserver/radarr:latest
container_name: radarr
restart: unless-stopped
ports:
- "7878:7878"
volumes:
# Config on SSD
- /home/jpmschweitzer/docker-data/radarr:/config
# Media and downloads on HDD
- /mnt/media/jellyfin/movies:/movies
- /mnt/media/downloads:/downloads
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
# PostgreSQL (postgres-shared)
- RADARR__POSTGRES__HOST=postgres-shared
- RADARR__POSTGRES__PORT=5432
- RADARR__POSTGRES__USER=media_user
- RADARR__POSTGRES__PASSWORD=${MEDIA_DB_PASSWORD}
- RADARR__POSTGRES__MAINDB=radarr
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:7878/ping || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
depends_on:
- sabnzbd
- prowlarr
networks:
- docker-dataplane
# ============================================================
# PROWLARR - Indexer Management
# ============================================================
prowlarr:
image: linuxserver/prowlarr:latest
container_name: prowlarr
restart: unless-stopped
ports:
- "9696:9696"
volumes:
# Config on SSD
- /home/jpmschweitzer/docker-data/prowlarr:/config
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
# PostgreSQL (postgres-shared)
- PROWLARR__POSTGRES__HOST=postgres-shared
- PROWLARR__POSTGRES__PORT=5432
- PROWLARR__POSTGRES__USER=media_user
- PROWLARR__POSTGRES__PASSWORD=${MEDIA_DB_PASSWORD}
- PROWLARR__POSTGRES__MAINDB=prowlarr
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:9696/ping || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- docker-dataplane
# ============================================================
# SABNZBD - Usenet Download Client
# ============================================================
sabnzbd:
image: linuxserver/sabnzbd:latest
container_name: sabnzbd
restart: unless-stopped
ports:
- "8880:8080" # Web UI (remapped to avoid Portainer conflict)
volumes:
# Config on SSD
- /home/jpmschweitzer/docker-data/sabnzbd:/config
# Downloads on HDD
- /mnt/media/downloads:/downloads
- /mnt/media/downloads/incomplete:/incomplete-downloads
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:8080/api?mode=version || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- docker-dataplane
networks:
docker-dataplane:
external: true
name: docker-dataplane
# ============================================================
# SETUP GUIDE
# ============================================================
#
# 0. Create PostgreSQL databases (run once):
# docker exec -it postgres-shared psql -U postgres -c "
# CREATE USER media_user WITH PASSWORD '<MEDIA_DB_PASSWORD>';
# CREATE DATABASE sonarr OWNER media_user;
# CREATE DATABASE radarr OWNER media_user;
# CREATE DATABASE prowlarr OWNER media_user;
# GRANT ALL PRIVILEGES ON DATABASE sonarr TO media_user;
# GRANT ALL PRIVILEGES ON DATABASE radarr TO media_user;
# GRANT ALL PRIVILEGES ON DATABASE prowlarr TO media_user;
# "
#
# 1. Create download directories:
# mkdir -p /mnt/media/downloads/complete /mnt/media/downloads/incomplete /mnt/media/downloads/usenet
# chown -R 1000:1000 /mnt/media/downloads
#
# 2. Create config directories:
# mkdir -p ~/docker-data/sonarr ~/docker-data/radarr ~/docker-data/prowlarr ~/docker-data/sabnzbd
# chown -R 1000:1000 ~/docker-data/sonarr ~/docker-data/radarr ~/docker-data/prowlarr ~/docker-data/sabnzbd
#
# 3. Set MEDIA_DB_PASSWORD environment variable in Portainer stack or export before deploy
#
# 4. Deploy the stack via Portainer or:
# docker stack deploy -c media.yml media
#
# 5. Configure services in order:
#
# a) SABnzbd (http://localhost:8880):
# - Complete setup wizard
# - Add Usenet server credentials
# - Configure download categories: tv, movies
# - Note the API key for Sonarr/Radarr
#
# b) Prowlarr (http://localhost:9696):
# - Add indexers (NZBgeek, DrunkenSlug, etc.)
# - Add Sonarr as application: http://sonarr:8989
# - Add Radarr as application: http://radarr:7878
# - Sync indexers to apps
#
# c) Sonarr (http://localhost:8989):
# - Settings → Media Management → Root Folder: /tv
# - Settings → Download Clients → Add SABnzbd:
# Host: sabnzbd, Port: 8080, API Key: <from SABnzbd>
# - Indexers will be synced from Prowlarr
#
# d) Radarr (http://localhost:7878):
# - Settings → Media Management → Root Folder: /movies
# - Settings → Download Clients → Add SABnzbd:
# Host: sabnzbd, Port: 8080, API Key: <from SABnzbd>
# - Indexers will be synced from Prowlarr
#
# e) Jellyfin (http://localhost:8096):
# - Already configured (migrated from previous stack)
# - Libraries should auto-detect new content
#
# 5. Inter-service communication (use container names):
# - SABnzbd from Sonarr/Radarr: http://sabnzbd:8080
# - Prowlarr from Sonarr: http://prowlarr:9696
# - All services on docker-dataplane network
#
# STORAGE LAYOUT:
# /mnt/media/
# ├── downloads/
# │ ├── complete/ # Completed downloads
# │ ├── incomplete/ # In-progress downloads
# │ └── usenet/ # Usenet-specific
# └── jellyfin/
# ├── movies/ # Radarr imports here
# └── series/ # Sonarr imports here