Self-contained stack with 25 containers: API server, console, realtime WebSocket server, 10 workers, 3 schedulers, maintenance task, browser, OpenRuntimes executor, plus internal MariaDB 10.11 and Redis 7.4. Exposed on port 8093 via bundled Traefik. Watchtower disabled for deliberate upgrades. Also includes media.yml reformatting and Jellyfin GPU deploy block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
276 lines
9.9 KiB
YAML
276 lines
9.9 KiB
YAML
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:
|
|
limits:
|
|
memory: 16G
|
|
reservations:
|
|
memory: 8G
|
|
devices:
|
|
- driver: nvidia
|
|
count: 1
|
|
capabilities: [gpu]
|
|
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
|
|
- HOST_WHITELIST=sabnzbd,localhost,192.168.86.149,tower-of-joy
|
|
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
|