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:
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user