# Media Server on Netcup RS 8000 A self-hosted media server stack with automated request management, running on Netcup RS 8000 with Jellyfin for streaming. ## Architecture ``` Users │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Cloudflare Tunnel → Traefik │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ Jellyfin │ │Jellyseerr │ │ Navidrome │ │ (Stream) │ │(Requests) │ │ (Music) │ └─────┬─────┘ └─────┬─────┘ └───────────┘ │ │ │ ▼ │ ┌─────────────────────┐ │ │ Request Flow │ │ │ ┌───────────────┐ │ │ │ │ Sonarr │ │ ← TV Shows │ │ │ Radarr │ │ ← Movies │ │ │ Lidarr │ │ ← Music │ │ └───────┬───────┘ │ │ │ │ │ │ │ ┌───────▼───────┐ │ │ │ │ Prowlarr │ │ ← Indexer Management │ │ └───────┬───────┘ │ │ │ │ │ │ │ ┌───────▼───────┐ │ │ │ │ qBittorrent │ │ ← Downloads (optional VPN) │ │ └───────┬───────┘ │ │ └──────────┼──────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Local NVMe Storage (3TB) │ │ /media/movies /media/shows /media/music │ └─────────────────────────────────────────────────────────────┘ ``` ## How It Works 1. **Users request media** via Jellyseerr (`requests.jeffemmett.com`) 2. **Jellyseerr forwards** requests to Sonarr/Radarr/Lidarr 3. **Prowlarr searches** configured indexers for the content 4. **qBittorrent downloads** the files (optionally through VPN) 5. **\*arr apps organize** files into proper folders 6. **Jellyfin detects** new media and makes it available for streaming ## Server Specs - **Netcup RS 8000 G12 Pro**: €45/month - **20 CPU cores** for transcoding - **64GB RAM** for caching - **3TB NVMe storage** for media - **1Gbps bandwidth** included ## Quick Start ### 1. Clone and Configure ```bash git clone https://gitea.jeffemmett.com/jeffemmett/jellyfin-media.git cd jellyfin-media cp .env.example .env ``` ### 2. Deploy to Netcup ```bash ./scripts/deploy-to-netcup.sh ``` ### 3. Add Cloudflare Tunnel Hostnames Add these hostnames to your Cloudflare tunnel config (`/root/cloudflared/config.yml`): ```yaml # Media Request System - hostname: requests.jeffemmett.com service: http://localhost:80 - hostname: invite.jeffemmett.com service: http://localhost:80 - hostname: sonarr.jeffemmett.com service: http://localhost:80 - hostname: radarr.jeffemmett.com service: http://localhost:80 - hostname: prowlarr.jeffemmett.com service: http://localhost:80 - hostname: lidarr.jeffemmett.com service: http://localhost:80 - hostname: downloads.jeffemmett.com service: http://localhost:80 ``` Then restart cloudflared: `docker restart cloudflared` ### 4. Configure Services (First Time Setup) #### Step 1: Configure Prowlarr (Indexers) 1. Go to `https://prowlarr.jeffemmett.com` 2. Add indexers (torrent sites you have access to) 3. Go to Settings → Apps → Add Radarr, Sonarr, Lidarr #### Step 2: Configure Download Client 1. Go to `https://downloads.jeffemmett.com` 2. Default login: `admin` / `adminadmin` (change immediately!) 3. Settings → Downloads → Default Save Path: `/downloads` #### Step 3: Configure Radarr (Movies) 1. Go to `https://radarr.jeffemmett.com` 2. Settings → Media Management → Root Folder: `/movies` 3. Settings → Download Clients → Add qBittorrent: - Host: `qbittorrent` - Port: `8080` #### Step 4: Configure Sonarr (TV Shows) 1. Go to `https://sonarr.jeffemmett.com` 2. Settings → Media Management → Root Folder: `/tv` 3. Settings → Download Clients → Add qBittorrent (same as above) #### Step 5: Configure Lidarr (Music) 1. Go to `https://lidarr.jeffemmett.com` 2. Settings → Media Management → Root Folder: `/music` 3. Settings → Download Clients → Add qBittorrent (same as above) #### Step 6: Configure Jellyseerr (Requests) 1. Go to `https://requests.jeffemmett.com` 2. Sign in with Jellyfin (use your Jellyfin server URL: `http://jellyfin:8096`) 3. Add Radarr/Sonarr servers: - Hostname: `radarr` or `sonarr` (internal Docker hostname) - Port: `7878` (Radarr) or `8989` (Sonarr) - API Key: Get from Radarr/Sonarr Settings → General #### Step 7: Configure Wizarr (User Invitations) 1. Go to `https://invite.jeffemmett.com` 2. Create your admin account on first visit 3. Connect to Jellyfin: - Server URL: `http://jellyfin:8096` - API Key: Get from Jellyfin → Dashboard → API Keys → Create 4. Create invite links: - Set expiration (1 day, 1 week, never, etc.) - Set usage limit (1 use, unlimited, etc.) - Share the generated link with friends 5. When friends use the link, they'll be guided through: - Creating their Jellyfin account - Downloading mobile apps - Connecting to your server ## Services | Service | URL | Description | |---------|-----|-------------| | Jellyfin | https://movies.jeffemmett.com | Video streaming (movies & TV) | | Navidrome | https://music.jeffemmett.com | Music streaming server | | Jellyseerr | https://requests.jeffemmett.com | Media request interface | | Wizarr | https://invite.jeffemmett.com | User invitation system | | Sonarr | https://sonarr.jeffemmett.com | TV show management | | Radarr | https://radarr.jeffemmett.com | Movie management | | Lidarr | https://lidarr.jeffemmett.com | Music management | | Prowlarr | https://prowlarr.jeffemmett.com | Indexer management | | qBittorrent | https://downloads.jeffemmett.com | Download client | ## Security Recommendations **Important:** The \*arr admin interfaces should be protected. Options: 1. **Cloudflare Access** (Recommended): Add authentication via Zero Trust dashboard 2. **SSH Tunnel**: Access admin UIs only via `ssh -L 8989:localhost:8989 netcup` 3. **Basic Auth**: Add Traefik middleware for HTTP basic auth To add Cloudflare Access protection: 1. Go to Cloudflare Zero Trust → Access → Applications 2. Create application for each admin subdomain 3. Add authentication policy (email, one-time PIN, etc.) ## VPN Support For download privacy, enable the VPN profile: ```bash # Add to .env: VPN_PROVIDER=mullvad VPN_WIREGUARD_PRIVATE_KEY=your_private_key VPN_WIREGUARD_ADDRESS=10.x.x.x/32 # Start with VPN: docker compose --profile vpn up -d ``` Then modify qBittorrent to route through gluetun: ```yaml # In docker-compose-server.yml, uncomment: network_mode: "service:gluetun" # And comment out the networks/labels sections ``` ## Folder Structure ``` /opt/media-server/ ├── media/ │ ├── movies/ # Movie files │ ├── shows/ # TV show files │ └── music/ # Music files ├── config/ │ ├── jellyfin/ # Jellyfin config │ ├── jellyseerr/ # Jellyseerr config │ ├── sonarr/ # Sonarr config │ ├── radarr/ # Radarr config │ ├── lidarr/ # Lidarr config │ ├── prowlarr/ # Prowlarr config │ └── qbittorrent/ # qBittorrent config ├── downloads/ │ └── complete/ # Completed downloads └── cache/ └── jellyfin/ # Transcoding cache ``` ## Upload Script Usage ```bash # From local machine ./scripts/upload-to-netcup.sh # Examples: ./scripts/upload-to-netcup.sh /home/jeffe/Shows shows ./scripts/upload-to-netcup.sh /home/jeffe/Movies movies ./scripts/upload-to-netcup.sh /home/jeffe/Music music ``` The script uses rsync for efficient incremental uploads. ## Mobile Apps ### Jellyfin (Video) - **Android**: Jellyfin for Android - **iOS**: Swiftfin, Jellyfin Mobile ### Navidrome (Music) Navidrome is Subsonic-compatible and provides a Spotify-like self-hosted music experience. #### Recommended: Symfonium (Android - ~$5) The best mobile experience for Navidrome. Features: - Spotify-like UI with gapless playback - Offline downloads with smart caching - Android Auto, Chromecast, Wear OS support - Synced lyrics display - Multiple server support **Setup:** 1. Install Symfonium from Google Play (~$5 one-time) 2. Add Server → Subsonic/Navidrome 3. Server URL: `https://music.jeffemmett.com` 4. Enter your Navidrome username/password #### Other Clients | Platform | App | Cost | Notes | |----------|-----|------|-------| | Android | Symfonium | ~$5 | Best experience, recommended | | Android | Ultrasonic | Free | Solid open-source alternative | | iOS | Amperfy | Free | Clean UI, CarPlay support | | iOS | play:Sub | ~$5 | Polished experience | | Desktop | Sonixd | Free | Electron, cross-platform | | Desktop | Sublime Music | Free | GTK-based, Linux-native | | Web | Built-in | Free | `https://music.jeffemmett.com` | ## Music Features ### Sharing Share tracks/albums with anyone (no account needed): 1. In Navidrome web UI, click any track/album 2. Click ⋮ menu → **Share** 3. Set expiration (or never) 4. Copy the public link ### Lyrics Lyrics are automatically fetched from LRCLIB and displayed in Symfonium during playback. ### Spotify Playlist Import Lidarr syncs with your Spotify playlists: 1. Go to `https://lidarr.jeffemmett.com` 2. Settings → Import Lists → Add → Spotify Playlists 3. Authenticate with Spotify 4. Select playlists to monitor 5. Lidarr downloads albums from artists in your playlists ### Scrobbling (Optional) Track your listening history with Last.fm or ListenBrainz: 1. In Navidrome: Settings → Personal → Link account 2. All plays from web UI and Symfonium will scrobble ## Troubleshooting ### Downloads not starting 1. Check Prowlarr has working indexers 2. Verify qBittorrent is accessible: `docker logs qbittorrent` 3. Check Radarr/Sonarr logs for errors ### Media not appearing in Jellyfin 1. Verify files are in correct folder (`/media/movies`, `/media/shows`) 2. Trigger library scan in Jellyfin → Dashboard → Libraries 3. Check file permissions: should be owned by PUID:PGID (1000:1000) ### VPN connection issues ```bash # Check gluetun logs docker logs gluetun # Verify VPN is working docker exec gluetun curl ifconfig.me ``` ## License MIT