> ## Content Index
> Fetch the complete content index at: https://notes.d4rkcyber.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Media Server
- URL: https://notes.d4rkcyber.com/media-server-jellyfin-storage-and-requests/
- Published: 2026-10-29T11:31:19.000Z
- Updated: 2026-10-29T13:25:34.000Z
- Description: My Media Server; Jellyfin stack, passed-through storage, IPTV setup, and how I manage a growing media library.
- Author: David Dami
- Tags: homelab, media-server, docker, self-hosted

### Jellyfin, Storage, and Requests

I wanted one place to watch my media, request new titles, and manage a library that keeps growing. This is the setup I’m running now: a Proxmox VM, Docker Compose, Jellyfin, and the supporting services around it. It’s a field note from my homelab, not a universal hardware prescription.

## The host

The media server runs in a VM on Proxmox. I pass the disks through to the VM and mount them there for Docker to use. The VM currently sees four CPUs and about 9.7 GiB of RAM; Linux reports the CPU model as a Ryzen 5 3600.

Media is split across two local filesystems. The 916 GB filesystem is mounted at `/mnt/wd1tb`; the 3.6 TB filesystem is mounted at `/mnt/wd4tb`. Movies and the `vulcan` library are on the first disk. TV, music, and downloads are on the second. The containers bind-mount those directories into their own paths, so Jellyfin sees `/data/movies`, `/data/tvshows`, and `/data/music`, while the management tools use shared `/movies`, `/tv`, and `/downloads` paths.

At the time of writing, the 916 GB filesystem is around 90% used and the 3.6 TB filesystem is around 99% used. An NFS share is mounted as well, but available storage doesn’t remove the need to decide what the library should keep.

## The stack

Jellyfin is the media server. Jellyseerr gives users a request interface. Radarr, Sonarr, and Lidarr manage movies, series, and music; Prowlarr centralizes indexer configuration; qBittorrent is the download client; and Bazarr handles subtitles. Spotiarr and Redis are also in the Compose project. Jellystat, with PostgreSQL, runs separately and provides usage statistics.

The containers share an external Docker network named `proxy`. Traefik uses the Compose labels to route to the apps. Configuration directories are persisted with bind mounts, and media/download directories are mounted into the services that need them. The example below keeps the real storage paths but replaces my internal hostnames with `example.invalid`; adjust those labels, mounts, and the external network for your own environment.

## IPTV

I use a Jellyfin plugin with Xtream Codes and M3U support to watch free-to-air channels. Playlist URLs and provider credentials are deliberately not included here. This describes my setup; it isn’t a guide to obtaining IPTV sources.

## Managing a library that outgrows its disks

Jellystat is a little like a Spotify Wrapped for Jellyfin: it helps me see what users are watching and what they’ve finished. I review that activity periodically and make retention decisions manually. I’m the owner and admin, so I make the final call on what stays. When storage is tight, I sometimes have to reject a download request.

I’m considering a six-month review rule: flag files that have been on the server longer than six months, review them, then decide whether to keep, delete, or archive them. That’s a possible improvement, not an automated rule I’ve deployed.

## Compose file

This is the current media-stack Compose file, with internal Traefik hostnames anonymized. It references Spotify credentials through environment variables; put real values in a private `.env` file on the server and don’t commit that file. The `proxy` network must already exist.

```yaml
services:
  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Dublin
    volumes:
      - ./radarr_config:/config
      - /mnt/media-server/movies:/movies
      - /mnt/media-server/downloads:/downloads
      - /mnt/media-server/vulcan:/vulcan
    ports:
      - "7878:7878"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.radarr.rule=Host(`radarr.example.invalid`)"
      - "traefik.http.routers.radarr.entrypoints=https"
      - "traefik.http.routers.radarr.tls=true"
      - "traefik.http.routers.radarr.tls.certresolver=cloudflare"
      - "traefik.http.services.radarr.loadbalancer.server.port=7878"

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Dublin
    volumes:
      - ./sonarr_config:/config
      - /mnt/media-server/shows:/tv
      - /mnt/media-server/downloads:/downloads
    ports:
      - "8989:8989"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.sonarr.rule=Host(`sonarr.example.invalid`)"
      - "traefik.http.routers.sonarr.entrypoints=https"
      - "traefik.http.routers.sonarr.tls=true"
      - "traefik.http.routers.sonarr.tls.certresolver=cloudflare"
      - "traefik.http.services.sonarr.loadbalancer.server.port=8989"

  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Dublin
    volumes:
      - ./prowlarr_config:/config
    ports:
      - "9696:9696"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.prowlarr.rule=Host(`prowlarr.example.invalid`)"
      - "traefik.http.routers.prowlarr.entrypoints=https"
      - "traefik.http.routers.prowlarr.tls=true"
      - "traefik.http.routers.prowlarr.tls.certresolver=cloudflare"
      - "traefik.http.services.prowlarr.loadbalancer.server.port=9696"

  jellyfin:
    image: lscr.io/linuxserver/jellyfin:latest
    container_name: jellyfin
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Dublin
    volumes:
      - ./jellyfin_config:/config
      - ./custom-jellyfin-web/web:/usr/share/jellyfin/web:ro
      - /mnt/media-server/shows:/data/tvshows
      - /mnt/media-server/movies:/data/movies
      - /mnt/media-server/vulcan:/data/vulcan
      - /mnt/media-server/music:/data/music
    ports:
      - "8096:8096"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jellyfin.rule=Host(`jellyfin.example.invalid`)"
      - "traefik.http.routers.jellyfin.entrypoints=https"
      - "traefik.http.routers.jellyfin.tls=true"
      - "traefik.http.routers.jellyfin.tls.certresolver=cloudflare"
      - "traefik.http.services.jellyfin.loadbalancer.server.port=8096"

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Dublin
      - WEBUI_PORT=8072
      - TORRENTING_PORT=6881
    volumes:
      - ./qbit_config:/config
      - /mnt/media-server/downloads:/downloads
    ports:
      - "8072:8072"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.qbittorrent.rule=Host(`qbit.example.invalid`)"
      - "traefik.http.routers.qbittorrent.entrypoints=https"
      - "traefik.http.routers.qbittorrent.tls=true"
      - "traefik.http.routers.qbittorrent.tls.certresolver=cloudflare"
      - "traefik.http.services.qbittorrent.loadbalancer.server.port=8072"

  jellyseerr:
    image: ghcr.io/seerr-team/seerr:latest
    container_name: jellyseerr
    environment:
      - LOG_LEVEL=debug
      - TZ=Europe/Dublin
      - PORT=5055
    volumes:
      - ./jellyseerr_config:/app/config
    ports:
      - "5055:5055"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jellyseerr.rule=Host(`jellyseerr.example.invalid`)"
      - "traefik.http.routers.jellyseerr.entrypoints=https"
      - "traefik.http.routers.jellyseerr.tls=true"
      - "traefik.http.routers.jellyseerr.tls.certresolver=cloudflare"
      - "traefik.http.services.jellyseerr.loadbalancer.server.port=5055"

  lidarr:
    image: lscr.io/linuxserver/lidarr:latest
    container_name: lidarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    volumes:
      - ./lidarr_config:/config
      - /mnt/media-server/music:/music
      - /mnt/media-server/downloads:/downloads
    ports:
      - "8686:8686"
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.lidarr.rule=Host(`lidarr.example.invalid`)"
      - "traefik.http.routers.lidarr.entrypoints=https"
      - "traefik.http.routers.lidarr.tls=true"
      - "traefik.http.routers.lidarr.tls.certresolver=cloudflare"
      - "traefik.http.services.lidarr.loadbalancer.server.port=8686"

  bazarr:
    image: lscr.io/linuxserver/bazarr:latest
    container_name: bazarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    volumes:
      - ./bazarr_config:/config
      - /mnt/media-server/shows:/tv
      - /mnt/media-server/movies:/movies
    ports:
      - "6767:6767"
    networks:
      - proxy
    restart: unless-stopped

  spotiarr:
    image: mralexandersaavedra/spotiarr:latest
    container_name: spotiarr
    environment:
      - SPOTIFY_CLIENT_ID=${SPOTIFY_CLIENT_ID}
      - SPOTIFY_CLIENT_SECRET=${SPOTIFY_CLIENT_SECRET}
      - SPOTIFY_REDIRECT_URI=${SPOTIFY_REDIRECT_URI}
      - PUID=1000
      - PGID=1000
    volumes:
      - /mnt/media-server/music:/downloads
    ports:
      - "3001:3000"
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.spotiarr.rule=Host(`spoti.example.invalid`)"
      - "traefik.http.routers.spotiarr.entrypoints=https"
      - "traefik.http.routers.spotiarr.tls=true"
      - "traefik.http.routers.spotiarr.tls.certresolver=cloudflare"
      - "traefik.http.services.spotiarr.loadbalancer.server.port=3000"
    depends_on:
      - redis
    restart: unless-stopped

  redis:
    image: redis:alpine
    container_name: spotiarr-redis
    restart: unless-stopped
    networks:
      - proxy

networks:
  proxy:
    external: true

```