services: jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin volumes: - ./config/jellyfin:/config - ./cache/jellyfin:/cache - ./media:/media networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.jellyfin.rule=Host(`movies.jeffemmett.com`)" - "traefik.http.routers.jellyfin.entrypoints=web" - "traefik.http.routers.jellyfin.middlewares=jellyfin-headers" - "traefik.http.services.jellyfin.loadbalancer.server.port=8096" - "traefik.http.middlewares.jellyfin-headers.headers.customRequestHeaders.X-Forwarded-Proto=https" - "traefik.docker.network=traefik-public" # Request Management - User-facing interface for media requests jellyseerr: image: fallenbagel/jellyseerr:latest container_name: jellyseerr restart: unless-stopped environment: - LOG_LEVEL=debug - TZ=Europe/Berlin volumes: - ./config/jellyseerr:/app/config networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.jellyseerr.rule=Host(`requests.jeffemmett.com`)" - "traefik.http.routers.jellyseerr.entrypoints=web" - "traefik.http.routers.jellyseerr.middlewares=jellyseerr-headers" - "traefik.http.services.jellyseerr.loadbalancer.server.port=5055" - "traefik.http.middlewares.jellyseerr-headers.headers.customRequestHeaders.X-Forwarded-Proto=https" - "traefik.docker.network=traefik-public" navidrome: image: deluan/navidrome:latest container_name: navidrome restart: unless-stopped user: 1000:1000 environment: - ND_SCANSCHEDULE=1h - ND_LOGLEVEL=info - ND_SESSIONTIMEOUT=24h - ND_BASEURL= - ND_ENABLETRANSCODINGCONFIG=true - ND_TRANSCODINGCACHESIZE=1GB - ND_IMAGECACHESIZE=500MB volumes: - ./config/navidrome:/data - ./media/music:/music:ro networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.navidrome.rule=Host(`music.jeffemmett.com`)" - "traefik.http.routers.navidrome.entrypoints=web" - "traefik.http.routers.navidrome.middlewares=navidrome-headers" - "traefik.http.services.navidrome.loadbalancer.server.port=4533" - "traefik.http.middlewares.navidrome-headers.headers.customRequestHeaders.X-Forwarded-Proto=https" - "traefik.docker.network=traefik-public" sonarr: image: linuxserver/sonarr:latest container_name: sonarr restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin volumes: - ./config/sonarr:/config - ./media/shows:/tv - ./downloads:/downloads networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.sonarr.rule=Host(`sonarr.jeffemmett.com`)" - "traefik.http.routers.sonarr.entrypoints=web" - "traefik.http.services.sonarr.loadbalancer.server.port=8989" - "traefik.docker.network=traefik-public" radarr: image: linuxserver/radarr:latest container_name: radarr restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin volumes: - ./config/radarr:/config - ./media/movies:/movies - ./downloads:/downloads networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.radarr.rule=Host(`radarr.jeffemmett.com`)" - "traefik.http.routers.radarr.entrypoints=web" - "traefik.http.services.radarr.loadbalancer.server.port=7878" - "traefik.docker.network=traefik-public" prowlarr: image: linuxserver/prowlarr:latest container_name: prowlarr restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin volumes: - ./config/prowlarr:/config networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.prowlarr.rule=Host(`prowlarr.jeffemmett.com`)" - "traefik.http.routers.prowlarr.entrypoints=web" - "traefik.http.services.prowlarr.loadbalancer.server.port=9696" - "traefik.docker.network=traefik-public" lidarr: image: linuxserver/lidarr:latest container_name: lidarr restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin volumes: - ./config/lidarr:/config - ./media/music:/music - ./downloads:/downloads networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.lidarr.rule=Host(`lidarr.jeffemmett.com`)" - "traefik.http.routers.lidarr.entrypoints=web" - "traefik.http.services.lidarr.loadbalancer.server.port=8686" - "traefik.docker.network=traefik-public" # VPN for download privacy (optional - enable in .env) gluetun: image: qmcgaw/gluetun:latest container_name: gluetun restart: unless-stopped cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun environment: - VPN_SERVICE_PROVIDER=${VPN_PROVIDER:-mullvad} - VPN_TYPE=wireguard - WIREGUARD_PRIVATE_KEY=${VPN_WIREGUARD_PRIVATE_KEY:-} - WIREGUARD_ADDRESSES=${VPN_WIREGUARD_ADDRESS:-} - SERVER_COUNTRIES=${VPN_COUNTRY:-Germany} - TZ=Europe/Berlin volumes: - ./config/gluetun:/gluetun ports: # qBittorrent WebUI - 8080:8080 # BitTorrent ports - 6881:6881 - 6881:6881/udp networks: - media-network profiles: - vpn # Download client (routes through VPN when vpn profile enabled) qbittorrent: image: linuxserver/qbittorrent:latest container_name: qbittorrent restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin - WEBUI_PORT=8080 volumes: - ./config/qbittorrent:/config - ./downloads:/downloads # When VPN enabled, use gluetun network # network_mode: "service:gluetun" networks: - media-network - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.qbittorrent.rule=Host(`downloads.jeffemmett.com`)" - "traefik.http.routers.qbittorrent.entrypoints=web" - "traefik.http.services.qbittorrent.loadbalancer.server.port=8080" - "traefik.docker.network=traefik-public" # Legacy Transmission (kept for compatibility, prefer qBittorrent) transmission: image: linuxserver/transmission:latest container_name: transmission restart: unless-stopped environment: - PUID=1000 - PGID=1000 - TZ=Europe/Berlin - TRANSMISSION_WEB_HOME=/web volumes: - ./config/transmission:/config - ./downloads:/downloads - ./watch:/watch ports: - 9091:9091 - 51413:51413 - 51413:51413/udp networks: - media-network profiles: - legacy networks: media-network: driver: bridge traefik-public: external: true