Deploying the Media Automation Stack
This guide details how to deploy the ultimate bare-metal home media server using Kubernetes. It demonstrates advanced concepts like dual-storage paradigms, internal vs external DNS, and solving permission mismatches.
The "Arr" Stack Architecture
The stack consists of several interoperating services:
- Jellyfin: The media player (like Netflix). Streams your downloaded files to your TV.
- Jellyseerr: The request portal. A beautiful UI for non-technical users to request movies and shows.
- Radarr: The movie manager. It searches the internet for movies requested via Jellyseerr and sends them to the downloader.
- Sonarr: The TV show manager. Operates exactly like Radarr but for episodic content.
- Prowlarr: The indexer manager. It holds the configurations for all your torrent sites (e.g., YTS, EZTV) and dynamically injects them into Radarr and Sonarr.
- qBittorrent: The download client. It accepts
.torrentfiles from Radarr/Sonarr, downloads them, and saves them to the shared network drive.
The Dual-Storage Paradigm
Because these applications manage massive multi-terabyte media files but also require blazing-fast database queries for their UIs, we use two entirely different Kubernetes storage provisioners simultaneously:
- Local-Path Provisioner (SSDs): Used for the
/configdirectory of every pod. This ensures the SQLite databases for Radarr/Sonarr are written directly to the fast physical SSD on the node, preventing UI lag. - NFS Provisioner (HDDs): Used for the
/data/mediadirectory. This is a massive, slow spinning disk exported via NFS. Radarr, Sonarr, qBittorrent, and Jellyfin all mount this exact same volume (usingReadWriteMany).
[!WARNING] The
root_squashPermission Trap The NFS provisioner creates the media folder asroot:root. Thelinuxserver.ioDocker images run as userabc(UID 1000). This will cause aPermission Deniederror when the containers try to write files. You must manually SSH into the NFS server and runsudo chown -R 1000:1000 /mnt/mediato grant them access!
Deployment Workflow
1. Apply Kubernetes Manifests
Apply the manifests to the media namespace. Each application requires a Deployment, a PVC for config, a PVC for media (shared), a Service, and an Ingress.
kubectl apply -f k8s/apps/media/jellyfin/
kubectl apply -f k8s/apps/media/prowlarr/
kubectl apply -f k8s/apps/media/radarr/
kubectl apply -f k8s/apps/media/sonarr/
kubectl apply -f k8s/apps/media/jellyseerr/
kubectl apply -f k8s/apps/media/qbittorrent/
2. Configure the "Brain" (Prowlarr)
Prowlarr must be configured first, as it provides the search engines to the rest of the stack.
- Open
http://prowlarr.homelab.local. - Add your preferred Indexers (e.g., YTS for movies, EZTV for shows).
- Note: If an indexer fails with
Connection Refused, your ISP is likely blocking the DNS. Try an alternate "Base URL" from the dropdown or change your router's DNS to1.1.1.1. - Go to Settings > Apps and add Radarr and Sonarr.
- Crucial Step: When defining the
Radarr ServerURL inside Prowlarr, you must use the internal Kubernetes DNS name (http://radarr:80), NOT the external Ingress URL.
3. Configure the Downloader (qBittorrent)
qBittorrent must save its files to the shared network drive, not its internal ephemeral container storage.
- Open
http://qbittorrent.homelab.local. - (Note: Check the pod logs for the temporary admin password if using v4.6.1+).
- Go to Settings > Downloads and change the Default Save Path from
/downloads/to/data/media/downloads/.
4. Link the Stack
- Open Radarr and Sonarr.
- Go to Settings > Download Clients and add qBittorrent.
- Host:
qbittorrent(Internal K8s DNS) - Port:
80(The K8s Service Port, which forwards to 8080) - Go to Settings > Media Management, scroll down to Root Folders, and define the base paths:
- Radarr:
/data/media/movies - Sonarr:
/data/media/tv
5. Final Result
When you request a movie in Jellyseerr:
1. Jellyseerr pings Radarr.
2. Radarr asks Prowlarr to search YTS.
3. Radarr finds the torrent and sends it to qbittorrent:80.
4. qBittorrent downloads it to /data/media/downloads/.
5. Radarr detects the finished download, moves it to /data/media/movies/, and renames it.
6. Jellyfin scans the folder and it appears on your TV!