Jellyseerr Multimedia Stack

Jellyseerr Multimedia Stack
Jellyseerr Multimedia Stack

Yes I have tested it and it works just fine, however it can be complicated to setup beyond just the stack. There is considerable work required that cant be automated with each service as it flows down the chain.

This stack is not for a novice but its not rocket science either, but yes it is a pain in the arse to configure as you have numerous services to get running and bug free for that "click" to work seamlessly.

Purpose

Find a movie or a series via Jelllyseer. Click on the appropriate icon. Watch a trailer, read the breakdown, request the movie. The rest should just happen. Simple.

This is your own multimedia server with a request function, and index function and it talks directly to Jellyfin. Nice, neat and tidy.

Yeah right.

Notes

  1. The stack is self explanatory
  2. You will need to setup your reverse proxy and your cname entries in your DNS server - do this first.
  3. You will need to setup Jellyseer via settings, adding Jellyfin, users, services, and so on.
  4. The qbittorrent server will require a new admin username and password. Sort this once the stack comes up, and dont forget the password. It requires hacking to reset it.
  5. The API Keys you will need to change and I cant remember how I did it. Sorry this is not my day job (I do this for fun to exercise my mind).
  6. Mine is CUDA enabled. You may need to remove this part.
  7. I built my stack for Podman not Docker but you should be able to figure it out.

The Multimedia Stack

As per below.

# =============================================================================
# Multimedia  Media Stack — baden.braedach.com
# =============================================================================
# Services:   Jellyfin, Jellyseerr, Sonarr, Radarr, Prowlarr, Bazarr,
#             Flaresolverr, qBittorrent
# Host:       baden — Proxmox LXC, NVIDIA RTX 3070 (CDI)
# Runtime:    Podman (rootful) with Docker Compose compatibility
# Network:    jellyfin-net (internal, defined in this file)
# UID/GID:    1000:1000 — set via PUID/PGID (linuxserver) or entrypoint env
# Proxy:      Zoraxy reverse proxy via Cloudflare tunnel (braedach.com)
# CNAME:      All CNAMEs point to proxy, not container IPs
#
# Reviewed:   10-05-2026
# Version:    1.0.0
# =============================================================================
#
# Volume layout expected on host (create before first run):
#   sudo mkdir -p \
#     /srv/jellyfin/{cache,config,media/movies,media/series,music} \
#     /srv/jellyseerr/config \
#     /srv/sonarr/config \
#     /srv/radarr/config \
#     /srv/prowlarr/config \
#     /srv/bazarr/config \
#     /srv/qbittorrent/{config,downloads,downloads/incomplete,plugins}
#   sudo chown -R 1000:1000 /srv/jellyfin /srv/jellyseerr /srv/sonarr \
#     /srv/radarr /srv/prowlarr /srv/bazarr /srv/qbittorrent
#
# GPU passthrough:
#   CDI configured via setup-lxc-gpu.sh on pxe/baden.
#   Jellyfin uses NVENC/NVDEC for hardware transcoding — enable in:
#     Dashboard > Playback > Transcoding > NVIDIA NVENC
#
# Podman notes:
#   - Do NOT add a 'user:' directive to linuxserver images — it conflicts
#     with s6-overlay init which handles UID/GID internally via PUID/PGID.
#   - Jellyfin uses its own entrypoint; pre-chown volumes as shown above.
#   - No :U flag needed — redundant in rootful Podman without user: set.
#   - FlareSolverr runs as root internally; PUID/PGID not supported.
#   - Watchtower disabled on all services — update manually or via script.
# =============================================================================

name: multimedia-stack

services:

  # ---------------------------------------------------------------------------
  # Jellyfin — Media Server
  # ---------------------------------------------------------------------------
  jellyfin:
    container_name: jellyfin
    hostname: jellyfin
    image: jellyfin/jellyfin:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=jellyfin
      - homepage.group=Media
      - homepage.name=Jellyfin
      - homepage.href=https://jellyfin.baden.braedach.com
      - homepage.icon=jellyfin.png
      - com.centurylinklabs.watchtower.enable=false
    ports:M Media 
      - "8096:8096"        # HTTP — primary UI / API
      - "8920:8920"        # HTTPS (TLS terminated at Zoraxy)
      - "7359:7359/udp"    # LAN client auto-discovery
      - "1900:1900/udp"    # DLNA SSDP multicast
    environment:
      - TZ=Australia/Perth
      - JELLYFIN_PublishedServerUrl=https://jellyfin.baden.braedach.com
    volumes:
      - /srv/jellyfin/cache:/cache
      - /srv/jellyfin/config:/config
      - /srv/jellyfin/media/movies:/movies
      - /srv/jellyfin/media/series:/series
      - /srv/jellyfin/music:/music
      - /etc/localtime:/etc/localtime:ro
    devices:
      # Podman CDI — NVIDIA RTX 3070 passthrough (configured via setup-lxc-gpu.sh)
      - nvidia.com/gpu=all
    cap_add:
      - SYS_TIME
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:8096/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # Jellyseerr — Media Request Management
  # ---------------------------------------------------------------------------
  jellyseerr:
    container_name: jellyseerr
    hostname: jellyseerr
    image: ghcr.io/seerr-team/seerr:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=jellyseerr
      - homepage.group=Media
      - homepage.name=Jellyseerr
      - homepage.href=https://jellyseerr.baden.braedach.com
      - homepage.icon=jellyseerr.png
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "5055:5055"
    environment:
      - TZ=Australia/Perth
      - LOG_LEVEL=info
      # Pre-seeds Jellyfin connection on first run (ignored if config already exists)
      - JELLYFIN_URL=http://jellyfin:8096
      - RADARR_URL=http://radarr:7878
      - RADARR_API_KEY=[your-radarr-api-key]
      - SONARR_URL=http://sonarr:8989
      - SONARR_API_KEY=[your-sonarr-api-key]
    volumes:
      - /srv/jellyseerr/config:/app/config
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      jellyfin:
        condition: service_healthy
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:5055/api/v1/settings/public || exit 1
      start_period: 20s
      timeout: 3s
      interval: 15s
      retries: 3
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # Sonarr — TV Series Automation
  # ---------------------------------------------------------------------------
  sonarr:
    container_name: sonarr
    hostname: sonarr
    image: lscr.io/linuxserver/sonarr:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=sonarr
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "8989:8989"
    environment:
      - TZ=Australia/Perth
      - PUID=1000
      - PGID=1000
      - SONARR__LOG__LEVEL=info
      - SONARR__ANALYTICS__ENABLED=false
      # External auth — Zoraxy handles access control; disable Sonarr's own login
      - SONARR__AUTH__METHOD=External
      - SONARR__AUTH__REQUIRED=DisabledForLocalAddresses
    volumes:
      - /srv/sonarr/config:/config
      - /srv/jellyfin/media/series:/series
      - /srv/qbittorrent/downloads:/downloads
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      - prowlarr
      - qbittorrent
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:8989/ping || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # Radarr — Movie Automation
  # ---------------------------------------------------------------------------
  radarr:
    container_name: radarr
    hostname: radarr
    image: lscr.io/linuxserver/radarr:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=radarr
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "7878:7878"
    environment:
      - TZ=Australia/Perth
      - PUID=1000
      - PGID=1000
      - RADARR__LOG__LEVEL=info
      - RADARR__ANALYTICS__ENABLED=false
      # External auth — Zoraxy handles access control; disable Radarr's own login
      - RADARR__AUTH__METHOD=External
      - RADARR__AUTH__REQUIRED=DisabledForLocalAddresses
    volumes:
      - /srv/radarr/config:/config
      - /srv/jellyfin/media/movies:/movies
      - /srv/qbittorrent/downloads:/downloads
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      - prowlarr
      - qbittorrent
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:7878/ping || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # Prowlarr — Indexer Management (feeds Sonarr + Radarr)
  # ---------------------------------------------------------------------------
  prowlarr:
    container_name: prowlarr
    hostname: prowlarr
    image: lscr.io/linuxserver/prowlarr:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=prowlarr
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "9696:9696"
    environment:
      - TZ=Australia/Perth
      - PUID=1000
      - PGID=1000
      - PROWLARR__LOG__LEVEL=info
      - PROWLARR__ANALYTICS__ENABLED=false
      # External auth — Zoraxy handles access control; disable Prowlarr's own login
      - PROWLARR__AUTH__METHOD=External
      - PROWLARR__AUTH__REQUIRED=DisabledForLocalAddresses
    volumes:
      - /srv/prowlarr/config:/config
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      flaresolverr:
        condition: service_healthy
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:9696/ping || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # Bazarr — Subtitle Automation (linked to Sonarr + Radarr)
  # ---------------------------------------------------------------------------
  bazarr:
    container_name: bazarr
    hostname: bazarr
    image: lscr.io/linuxserver/bazarr:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=bazarr
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "6767:6767"
    environment:
      - TZ=Australia/Perth
      - PUID=1000
      - PGID=1000
      - BAZARR__LOG__LEVEL=info
    volumes:
      - /srv/bazarr/config:/config
      - /srv/jellyfin/media/movies:/movies
      - /srv/jellyfin/media/series:/series
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      sonarr:
        condition: service_healthy
      radarr:
        condition: service_healthy
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:6767/api/v1/status || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # FlareSolverr — Cloudflare Challenge Bypass (used by Prowlarr)
  # ---------------------------------------------------------------------------
  flaresolverr:
    container_name: flaresolverr
    hostname: flaresolverr
    image: ghcr.io/flaresolverr/flaresolverr:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=flaresolverr
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "8191:8191"
    environment:
      - TZ=Australia/Perth
      - LOG_LEVEL=info
      - LOG_HTML=false
      - CAPTCHA_SOLVER=none
    # FlareSolverr runs as root internally; PUID/PGID not supported
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:8191/ || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s
    networks:
      - jellyfin-net

  # ---------------------------------------------------------------------------
  # qBittorrent — Download Client
  # ---------------------------------------------------------------------------
  # Plugin notes:
  #   Search plugins are Python scripts installed at:
  #     /config/data/nova3/engines/   (linuxserver image path)
  #   The /plugins bind-mount maps /srv/qbittorrent/plugins on the host to
  #   that path, making plugin installation/removal host-manageable.
  #   Python 3 is required for search plugins — provided by the
  #   linuxserver/mods:qbittorrent-search-plugins docker mod below.
  #
  #   Incomplete downloads land in /downloads/incomplete and are moved to
  #   /downloads on completion. Configure both paths in:
  #     Tools > Options > Downloads > Save files to / Keep incomplete in
  # ---------------------------------------------------------------------------
  qbittorrent:
    container_name: qbittorrent
    hostname: qbittorrent
    image: lscr.io/linuxserver/qbittorrent:latest
    restart: always
    labels:
      - com.docker.compose.project=jellyfin-stack
      - com.docker.compose.service=qbittorrent
      - homepage.group=Media Automation
      - homepage.name=qBittorrent
      - homepage.href=https://qbittorrent.baden.braedach.com
      - homepage.icon=qbittorrent.png
      - com.centurylinklabs.watchtower.enable=false
    ports:
      - "8980:8980"        # Web UI
      - "6881:6881"        # BitTorrent TCP
      - "6881:6881/udp"    # BitTorrent UDP
    environment:
      - TZ=Australia/Perth
      - PUID=1000
      - PGID=1000
      - WEBUI_PORT=8980
      # Linuxserver mod — installs Python 3 + search plugin support
      - DOCKER_MODS=linuxserver/mods:qbittorrent-search-plugins
    volumes:
      - /srv/qbittorrent/config:/config
      - /srv/qbittorrent/downloads:/downloads
      - /srv/qbittorrent/downloads/incomplete:/downloads/incomplete
      - /srv/qbittorrent/plugins:/config/data/nova3/engines
      - /etc/localtime:/etc/localtime:ro
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:8980/ || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 45s
    networks:
      - jellyfin-net

networks:
  jellyfin-net:
    name: jellyfin-net
    driver: bridge

Prerequisites

  1. You need some serious storage - this is local in this configuration
  2. CUDA support for Jellyfin is not standard - refer to their documentation
  3. FIrewall exemptions - depending on your firewall it can get rather annoying
  4. Geoblocking can greatly affect the performance of your P2P server. Consideration is required.

I will not expand on this post. It is simply an example of some of the stuff that I like playing with, and its pushing my dated server rather hard, when fully employed.

Anyway - have fun.

#enoughsaid