# Media Stack Kubernetes Deployment A complete self-hosted media solution deployed on Kubernetes. ## Stack Components | Component | Purpose | Port | Image | |-----------|---------|------|-------| | **Jellyfin** | Media server + Live TV frontend | 8096 | jellyfin/jellyfin:10.11.5 | | **Sonarr** | TV show management | 8989 | lscr.io/linuxserver/sonarr:latest | | **Radarr** | Movie management | 7878 | lscr.io/linuxserver/radarr:latest | | **Lidarr** | Music management | 8686 | lscr.io/linuxserver/lidarr:latest | | **Prowlarr** | Indexer management | 9696 | lscr.io/linuxserver/prowlarr:latest | | **qBittorrent** | Download client | 8080 | lscr.io/linuxserver/qbittorrent:latest | | **Dispatcharr** | IPTV/M3U proxy for live TV | 9191 | ghcr.io/dispatcharr/dispatcharr:latest | ## Network Configuration - Network: `10.0.0.0/24` - K8s Control Plane: `10.0.0.69` (k8scontrol) - K8s Workers: `10.0.0.70-73` (k8sworker1-3) - NFS Server: `10.0.0.230` - Kubernetes Version: 1.35 ## NFS Share Structure Your NFS server (10.0.0.230) should have these directories: ``` /srv/nfs/media/ ├── config/ │ ├── jellyfin/ │ ├── sonarr/ │ ├── radarr/ │ ├── lidarr/ │ ├── prowlarr/ │ ├── qbittorrent/ │ └── dispatcharr/ ├── downloads/ │ ├── complete/ │ └── incomplete/ ├── media/ │ ├── movies/ │ ├── tv/ │ └── music/ └── transcode/ ``` ## Pre-requisites ### 1. Configure NFS Server (10.0.0.230) ```bash # On NFS server sudo mkdir -p /srv/nfs/media/{config/{jellyfin,sonarr,radarr,lidarr,prowlarr,qbittorrent,dispatcharr},downloads/{complete,incomplete},media/{movies,tv,music},transcode} # Set permissions (adjust UID/GID as needed - 1000:1000 is common) sudo chown -R 1000:1000 /srv/nfs/media # Add to /etc/exports echo "/srv/nfs/media 10.0.0.0/24(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports # Apply exports sudo exportfs -ra ``` ### 2. Install NFS Client on K8s Nodes ```bash # On each worker node (k8sworker1-3) sudo apt-get update && sudo apt-get install -y nfs-common ``` ### 3. Verify NFS Connectivity ```bash # From any worker node showmount -e 10.0.0.230 ``` ## Deployment ### Manual Deployment (in order) **Without VPN:** ```bash # Create namespace kubectl apply -f base/namespace.yaml # Create storage kubectl apply -f base/nfs-storage.yaml # Create config kubectl apply -f base/configmap.yaml # Create PVCs kubectl apply -f base/pvcs.yaml # Deploy applications kubectl apply -f base/jellyfin.yaml kubectl apply -f base/sonarr.yaml kubectl apply -f base/radarr.yaml kubectl apply -f base/lidarr.yaml kubectl apply -f base/prowlarr.yaml kubectl apply -f base/qbittorrent.yaml kubectl apply -f base/dispatcharr.yaml ``` **With VPN (after configuring VPN credentials in `base/vpn/mullvad-secret.yaml`):** Download a .conf file from your Mullvad account and update the private key and wireguard address in `base/vpn/mullvad-secret.yaml`) ```bash # Create namespace kubectl apply -f base/namespace.yaml # Create storage kubectl apply -f base/nfs-storage.yaml # Create config kubectl apply -f base/configmap.yaml # Create VPN-specific resources kubectl apply -f base/vpn/gluetun-config.yaml kubectl apply -f base/vpn/qbittorrent-init-configmap.yaml kubectl apply -f base/vpn/mullvad-secret.yaml # Create PVCs kubectl apply -f base/pvcs.yaml # Deploy non-VPN applications kubectl apply -f base/jellyfin.yaml kubectl apply -f base/sonarr.yaml kubectl apply -f base/radarr.yaml kubectl apply -f base/lidarr.yaml # Deploy VPN-enabled applications kubectl apply -f base/vpn/prowlarr-vpn.yaml kubectl apply -f base/vpn/qbittorrent-vpn.yaml kubectl apply -f base/vpn/dispatcharr-vpn.yaml ``` ### Automated deployment A [deploy.sh](https://git.96-fromsofia.net/Bash/deploy-media-stack-k8s/src/branch/master/deploy.sh) script can be used as such: **Without VPN:** ```bash ./deploy.sh ``` **With VPN (recommended for qBittorrent, Prowlarr, Dispatcharr):** ```bash ./deploy.sh --vpn ``` Optionally to change the VPN location amend `SERVER_CITIES` in the `base/vpn/gluetun-config.yaml` file. To enable port forwarding: 1. Go to your mullvad.net account and navigate to `account/ports` 2. Add a port for your wireguard key 3. Amend the `FIREWALL_VPN_INPUT_PORTS` value to your port in the `base/vpn/gluetun-config.yaml` file 4. Configure qBittorrent to point to your client ## Accessing Services After deployment, services are available via NodePort: | Service | URL | |---------|-----| | Jellyfin | http://10.0.0.70:30096 | | Sonarr | http://10.0.0.70:30989 | | Radarr | http://10.0.0.70:30878 | | Lidarr | http://10.0.0.70:30686 | | Prowlarr | http://10.0.0.70:30696 | | qBittorrent | http://10.0.0.70:30080 | | Dispatcharr | http://10.0.0.70:30191 | *Replace 10.0.0.70 with any worker node IP* ## Post-Deployment Configuration ### 1. qBittorrent - Check container logs for temporary password: `kubectl logs -n media deployment/qbittorrent` - Login and change password immediately - Set download paths to `/downloads/complete` and `/downloads/incomplete` ### 2. Prowlarr - Add your indexers - Connect to Sonarr, Radarr, and Lidarr via Settings → Apps - Use internal service names: `http://sonarr:8989`, `http://radarr:7878`, `http://lidarr:8686` ### 3. Sonarr/Radarr/Lidarr - Add qBittorrent as download client: `http://qbittorrent:8080` - Set media library paths: `/media/tv`, `/media/movies`, `/media/music` - Configure quality profiles and root folders ### 4. Jellyfin - Run initial setup wizard - Add media libraries pointing to `/media/movies`, `/media/tv`, `/media/music` - For Live TV: Settings → Live TV → Add Tuner Device → HDHomeRun - Use Dispatcharr's HDHomeRun URL: `http://dispatcharr:9191` ### 5. Dispatcharr - Add your M3U playlist sources - Configure EPG sources - Enable channels you want to watch ## Useful Commands ```bash # Restart a deployment kubectl rollout restart deployment/jellyfin -n media # Scale down all (for maintenance) kubectl scale deployment --all -n media --replicas=0 # Scale back up kubectl scale deployment --all -n media --replicas=1 ``` ## Troubleshooting ### Permission Denied Errors - Ensure PUID/PGID in configmap matches NFS share ownership - Check that `no_root_squash` is set in NFS exports ## Upgrading To upgrade images: ```bash # Update a single deployment kubectl set image deployment/jellyfin -n media jellyfin=jellyfin/jellyfin:NEW_VERSION # Or edit the YAML and reapply kubectl apply -f base/jellyfin.yaml ``` ## Cleanup ```bash # Delete everything kubectl delete namespace media # This will remove all deployments, services, and PVCs # PVs and NFS data will remain ```