- Move sonarr, radarr, lidarr, prowlarr to *.jefflix.lol - Move jellyseerr (requests) and qbittorrent (downloads) to jefflix.lol - Add Hetzner Storage Box SSHFS mount scripts for u521871 - Add media-archive.sh for content migration between local and Hetzner - Update all volume mounts to use /mnt/hetzner-media for media storage - Update README with new URLs and tiered storage documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
||
|---|---|---|
| scripts | ||
| .env.example | ||
| .gitignore | ||
| README.md | ||
| docker-compose-server.yml | ||
README.md
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 │
├─────────────────────────────────────────────────────────────┤
│ Hetzner Storage Box (Optional Tier) │
│ /media/archive/movies /media/archive/shows (Cold storage)│
└─────────────────────────────────────────────────────────────┘
How It Works
- Users request media via Jellyseerr (
requests.jefflix.lol) - Jellyseerr forwards requests to Sonarr/Radarr/Lidarr
- Prowlarr searches configured indexers for the content
- qBittorrent downloads the files (optionally through VPN)
- *arr apps organize files into proper folders
- 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
git clone https://gitea.jeffemmett.com/jeffemmett/jellyfin-media.git
cd jellyfin-media
cp .env.example .env
2. Deploy to Netcup
./scripts/deploy-to-netcup.sh
3. Add Cloudflare Tunnel Hostnames
Add these hostnames to your Cloudflare tunnel config (/root/cloudflared/config.yml):
# Media Request System
- hostname: requests.jefflix.lol
service: http://localhost:80
- hostname: invite.jefflix.lol
service: http://localhost:80
- hostname: sonarr.jefflix.lol
service: http://localhost:80
- hostname: radarr.jefflix.lol
service: http://localhost:80
- hostname: prowlarr.jefflix.lol
service: http://localhost:80
- hostname: lidarr.jefflix.lol
service: http://localhost:80
- hostname: downloads.jefflix.lol
service: http://localhost:80
Then restart cloudflared: docker restart cloudflared
4. Configure Services (First Time Setup)
Step 1: Configure Prowlarr (Indexers)
- Go to
https://prowlarr.jefflix.lol - Add indexers (torrent sites you have access to)
- Go to Settings → Apps → Add Radarr, Sonarr, Lidarr
Step 2: Configure Download Client
- Go to
https://downloads.jefflix.lol - Default login:
admin/adminadmin(change immediately!) - Settings → Downloads → Default Save Path:
/downloads
Step 3: Configure Radarr (Movies)
- Go to
https://radarr.jefflix.lol - Settings → Media Management → Root Folder:
/movies - Settings → Download Clients → Add qBittorrent:
- Host:
qbittorrent - Port:
8080
- Host:
Step 4: Configure Sonarr (TV Shows)
- Go to
https://sonarr.jefflix.lol - Settings → Media Management → Root Folder:
/tv - Settings → Download Clients → Add qBittorrent (same as above)
Step 5: Configure Lidarr (Music)
- Go to
https://lidarr.jefflix.lol - Settings → Media Management → Root Folder:
/music - Settings → Download Clients → Add qBittorrent (same as above)
Step 6: Configure Jellyseerr (Requests)
- Go to
https://requests.jefflix.lol - Sign in with Jellyfin (use your Jellyfin server URL:
http://jellyfin:8096) - Add Radarr/Sonarr servers:
- Hostname:
radarrorsonarr(internal Docker hostname) - Port:
7878(Radarr) or8989(Sonarr) - API Key: Get from Radarr/Sonarr Settings → General
- Hostname:
Step 7: Configure Wizarr (User Invitations)
- Go to
https://invite.jeffemmett.com - Create your admin account on first visit
- Connect to Jellyfin:
- Server URL:
http://jellyfin:8096 - API Key: Get from Jellyfin → Dashboard → API Keys → Create
- Server URL:
- Create invite links:
- Set expiration (1 day, 1 week, never, etc.)
- Set usage limit (1 use, unlimited, etc.)
- Share the generated link with friends
- 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.jefflix.lol | Media request interface |
| Wizarr | https://invite.jeffemmett.com | User invitation system |
| Sonarr | https://sonarr.jefflix.lol | TV show management |
| Radarr | https://radarr.jefflix.lol | Movie management |
| Lidarr | https://lidarr.jefflix.lol | Music management |
| Prowlarr | https://prowlarr.jefflix.lol | Indexer management |
| qBittorrent | https://downloads.jefflix.lol | Download client |
Security Recommendations
Important: The *arr admin interfaces should be protected. Options:
- Cloudflare Access (Recommended): Add authentication via Zero Trust dashboard
- SSH Tunnel: Access admin UIs only via
ssh -L 8989:localhost:8989 netcup - Basic Auth: Add Traefik middleware for HTTP basic auth
To add Cloudflare Access protection:
- Go to Cloudflare Zero Trust → Access → Applications
- Create application for each admin subdomain
- Add authentication policy (email, one-time PIN, etc.)
VPN Support
For download privacy, enable the VPN profile:
# 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:
# 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
/mnt/hetzner-media/ # Hetzner Storage Box (optional)
└── archive/
├── movies/ # Archived movies
├── shows/ # Archived TV shows
└── music/ # Archived music
Tiered Storage with Hetzner Storage Box
For additional storage capacity, you can add a Hetzner Storage Box as an archive tier. This is cost-effective (~€3/TB/month) and keeps older content accessible without filling up local NVMe.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Jellyfin Libraries │
├─────────────────────────────────────────────────────────────┤
│ │
│ /media (Local NVMe - Fast) /media/archive (Hetzner) │
│ ├── movies/ ← Active ├── movies/ ← Archived │
│ ├── shows/ ← Recent ├── shows/ ← Old seasons │
│ └── music/ ← Favorites └── music/ ← Full lib │
│ │
│ Hot storage: New downloads Cold storage: Older media │
│ Fast transcoding & streaming Slower but cost-effective │
└─────────────────────────────────────────────────────────────┘
Setup
-
Sign up for Hetzner Storage Box: https://www.hetzner.com/storage/storage-box/
- Recommended: BX21 (5-10TB) for ~€11/month
- Enable SMB/CIFS in Hetzner Console
-
Run setup script on Netcup:
ssh netcup cd /opt/media-server sudo ./scripts/setup-hetzner-storage.sh -
Restart media stack:
docker compose down && docker compose up -d -
Add archive library in Jellyfin:
- Dashboard → Libraries → Add Media Library
- Content type: Movies (or Shows)
- Folder:
/media/archive/movies
Managing Archived Content
Use the media-archive.sh script to move content between local and Hetzner:
# Check storage status
./scripts/media-archive.sh status
# List content older than 90 days
./scripts/media-archive.sh list-old
# Archive a specific movie
./scripts/media-archive.sh archive movies "The Matrix (1999)"
# Archive all shows older than 180 days
./scripts/media-archive.sh archive shows --older-than 180
# Restore content from archive
./scripts/media-archive.sh restore movies "The Matrix (1999)"
# Backup all local media to Hetzner
./scripts/media-archive.sh sync
Pricing Reference
| Plan | Storage | Monthly |
|---|---|---|
| BX11 | 1 TB | ~€3.50 |
| BX21 | 5-10 TB | ~€11 |
| BX31 | 20 TB | ~€25 |
| BX41 | 40 TB | ~€47 |
Upload Script Usage
# From local machine
./scripts/upload-to-netcup.sh <local-path> <media-type>
# 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:
- Install Symfonium from Google Play (~$5 one-time)
- Add Server → Subsonic/Navidrome
- Server URL:
https://music.jeffemmett.com - 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):
- In Navidrome web UI, click any track/album
- Click ⋮ menu → Share
- Set expiration (or never)
- 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:
- Go to
https://lidarr.jefflix.lol - Settings → Import Lists → Add → Spotify Playlists
- Authenticate with Spotify
- Select playlists to monitor
- Lidarr downloads albums from artists in your playlists
Scrobbling (Optional)
Track your listening history with Last.fm or ListenBrainz:
- In Navidrome: Settings → Personal → Link account
- All plays from web UI and Symfonium will scrobble
Troubleshooting
Downloads not starting
- Check Prowlarr has working indexers
- Verify qBittorrent is accessible:
docker logs qbittorrent - Check Radarr/Sonarr logs for errors
Media not appearing in Jellyfin
- Verify files are in correct folder (
/media/movies,/media/shows) - Trigger library scan in Jellyfin → Dashboard → Libraries
- Check file permissions: should be owned by PUID:PGID (1000:1000)
VPN connection issues
# Check gluetun logs
docker logs gluetun
# Verify VPN is working
docker exec gluetun curl ifconfig.me
License
MIT