Manual Deploy¶
Deploy and configure apps with the Juju CLI. More control, more fun.
Think of it like connecting Lego blocks. Each charm is a block, and relations snap them together. It's slower and more effort than the HCL bundles, but by the end you'll know exactly what's deployed, what's connected to what, and why.
Perfect for learning, debugging, or building something the pre-built bundles don't cover.
Tip
Open a second terminal and run juju status --integrations --watch 1s to watch your deployment come alive as you run commands.
1. Infrastructure Apps¶
First, we lay the groundwork: shared storage and VPN.
Storage¶
Shared storage enables hardlinks between download clients and media managers. See Storage for why this matters.
Configure based on your storage backend:
Hostpath (recommended for single-node):
Native NFS (recommended for multi-node):
StorageClass (CSI driver):
File Ownership (Hostpath & NFS)
For hostpath and NFS backends, the storage path must be owned by UID/GID 1000:1000 by default:
If your path is owned by a different UID/GID, configure the storage charm to match:
# Check current ownership
ls -ln /path/to/your/media
# Configure storage charm with the actual UID/GID
juju config storage puid=1001 pgid=1001
For NFS, ensure the NFS export allows write access for the configured PUID/PGID.
For StorageClass with CSI drivers, this is driver-dependent. Block storage drivers typically handle ownership automatically, while shared filesystem drivers (CephFS, NFS-based CSI) follow the same rules as NFS.
VPN Gateway¶
The VPN gateway anonymizes external traffic from privacy-sensitive charms. See VPN Gateway for how it works.
Deploy gluetun:
Create and grant the VPN secret (key is encrypted at rest):
Configure gluetun:
juju config gluetun \
wireguard-private-key-secret=secret:vpn-key \
vpn-provider=protonvpn \
cluster-cidrs="10.1.0.0/16,10.152.183.0/24,192.168.1.0/24"
See VPN Provider and Cluster CIDRs for help determining these values.
OpenVPN Support
OpenVPN is not officially supported. If your VPN provider only supports OpenVPN, or you need to pass custom environment variables to Gluetun, use the custom-overrides config to enter override mode. In override mode, WireGuard validation is bypassed and the provided JSON is merged on top of the charm's built-in environment.
Unlike wireguard-private-key-secret which is stored as a Juju secret and encrypted at rest, custom-overrides is plain text charm config. Credentials passed here are not encrypted.
juju config gluetun \
vpn-provider=protonvpn \
cluster-cidrs="10.1.0.0/16,10.152.183.0/24,192.168.1.0/24" \
custom-overrides='{"VPN_TYPE": "openvpn", "OPENVPN_USER": "your-username", "OPENVPN_PASSWORD": "your-password"}'
Override mode relaxes config validation. Misconfiguration may result in silent failures that require inspecting the Gluetun container logs to diagnose. See the Gluetun wiki for available environment variables.
2. Media Apps¶
Now for the fun part: the actual media apps.
Download Clients¶
juju deploy qbittorrent-k8s --trust --channel=1/stable qbittorrent
juju deploy sabnzbd-k8s --trust --channel=1/stable sabnzbd
Configure ingress paths and credential rotation:
juju config qbittorrent ingress-path=/qbt credential-rotation=monthly
juju config sabnzbd ingress-path=/sab credential-rotation=monthly
credential-rotation automatically rotates credentials on the specified interval (disabled, daily, monthly, yearly) and syncs to related apps. See Secrets for how rotation works.
Want more download clients? Deploy additional instances with different names.
Media Managers¶
juju deploy radarr-k8s --trust --channel=1/stable radarr
juju deploy sonarr-k8s --trust --channel=1/stable sonarr
Configure variant and trash profiles:
| Variant | Root Folder | Default TRaSH Profile |
|---|---|---|
standard |
/data/media/movies or /data/media/tv | None |
4k |
/data/media/movies-uhd or /data/media/tv-uhd | uhd-bluray-web |
anime |
/data/media/anime/movies or /data/media/anime/tv | anime |
# Standard HD instance (set trash-profiles manually)
juju config radarr variant=standard trash-profiles=hd-bluray-web
# 4K instance (uses uhd-bluray-web by default)
juju deploy radarr-k8s --trust --channel=1/stable radarr-4k
juju config radarr-4k variant=4k
Deploy as many managers as needed with unique names and ingress paths.
Indexer¶
juju deploy prowlarr-k8s --trust --channel=1/stable prowlarr
juju deploy flaresolverr-k8s --trust --channel=1/stable flaresolverr
Media Server & Requester¶
juju deploy plex-k8s --trust --channel=1/stable plex
juju deploy overseerr-k8s --trust --channel=1/stable overseerr
Enable hardware transcoding for Plex (requires Intel QuickSync and Plex Pass):
3. Connecting Apps¶
Apps are deployed but isolated. Relations snap them together. This is where the Lego blocks click.
When you integrate two apps, they exchange information like URLs, API keys, and configuration details. This is how Radarr knows where to find qBittorrent, how Prowlarr registers itself with Sonarr, and how Overseerr discovers your media managers. No manual copying of URLs or API keys in web UIs.
Sensitive information (API keys, credentials) is shared via Juju secrets and encrypted at rest.
Storage¶
Connect apps to shared media storage. Required for hardlinks and atomic copies.
juju integrate radarr:media-storage storage:media-storage
juju integrate sonarr:media-storage storage:media-storage
juju integrate plex:media-storage storage:media-storage
juju integrate qbittorrent:media-storage storage:media-storage
juju integrate sabnzbd:media-storage storage:media-storage
VPN Tunnel¶
Connect download clients and indexer to VPN. Routes traffic through Gluetun's tunnel with kill switch protection.
juju integrate qbittorrent:vpn-gateway gluetun:vpn-gateway
juju integrate sabnzbd:vpn-gateway gluetun:vpn-gateway
juju integrate prowlarr:vpn-gateway gluetun:vpn-gateway
A two-way killswitch protects your privacy:
- If the VPN connection drops, Gluetun's internal killswitch blocks traffic
- If the Gluetun pod dies, Kubernetes NetworkPolicies block traffic
Verify pod external IP (should show VPN IP, not your real IP):
kubectl exec -n charmarr deploy/qbittorrent -- wget -qO- ifconfig.me
kubectl exec -n charmarr deploy/sabnzbd -- wget -qO- ifconfig.me
kubectl exec -n charmarr deploy/prowlarr -- wget -qO- ifconfig.me
Skipping VPN
If you use a different tunneling solution, skip Gluetun deployment and VPN integrations. You must enable unsafe-mode on qBittorrent and SABnzbd for them to start without VPN protection:
Warning
Without VPN integration, your real IP is exposed to torrent trackers and usenet providers.
Cloudflare Bypass¶
Connect FlareSolverr to Prowlarr. Solves captchas for indexers behind Cloudflare protection.
Download Clients to Managers¶
Connect Radarr/Sonarr to qBittorrent and SABnzbd. Sends download requests and monitors progress.
juju integrate radarr:download-client qbittorrent:download-client
juju integrate radarr:download-client sabnzbd:download-client
juju integrate sonarr:download-client qbittorrent:download-client
juju integrate sonarr:download-client sabnzbd:download-client
Indexer to Managers¶
Connect Prowlarr to Radarr/Sonarr. Syncs indexers automatically across all managers.
juju integrate radarr:media-indexer prowlarr:media-indexer
juju integrate sonarr:media-indexer prowlarr:media-indexer
Media Server to Managers¶
Connect Plex to Radarr/Sonarr. Adds required libraries automatically to Plex.
juju integrate plex:media-manager radarr:media-manager
juju integrate plex:media-manager sonarr:media-manager
Requester to Managers & Server¶
Connect Overseerr to Radarr/Sonarr and Plex. Enables user requests and library visibility.
juju integrate overseerr:media-manager radarr:media-manager
juju integrate overseerr:media-manager sonarr:media-manager
juju integrate overseerr:media-server plex:media-server
4. Istio (Optional)¶
Istio provides ingress and service mesh security. Check the Compatibility Checklist before enabling. If you skip Istio, handle ingress yourself (e.g., Nginx Ingress, Traefik, or NodePorts).
Path Prefixes
Path prefixes default to the Juju app name (e.g., deploying as radarr gives path /radarr):
| App Name | Default Path |
|---|---|
| radarr | /radarr |
| sonarr | /sonarr |
| prowlarr | /prowlarr |
| qbittorrent | /qbittorrent |
| sabnzbd | /sabnzbd |
With Istio ingress, these paths are automatically configured. If you're using your own ingress controller, configure it to route these paths to the respective services.
To use different paths, or set / to serve at root (no path prefix):
The ingress listener port defaults to 80. To change it:
Deploy Istio¶
Control Plane:
Warning
Skip istio-k8s if your cluster already has an Istiod control plane. If you are unsure, you don't have one.
Ingress Gateways:
juju deploy istio-ingress-k8s --trust --channel=2/stable arr-ingress
juju deploy istio-ingress-k8s --trust --channel=2/stable plex-ingress
juju deploy istio-ingress-k8s --trust --channel=2/stable overseerr-ingress
Beacon (for service mesh):
Required only for mTLS and authorization policies. See Networking for details.
Connect Ingress¶
Expose app UIs via LoadBalancer IPs.
# Arr apps and download clients
juju integrate radarr:istio-ingress-route arr-ingress:istio-ingress-route
juju integrate sonarr:istio-ingress-route arr-ingress:istio-ingress-route
juju integrate prowlarr:istio-ingress-route arr-ingress:istio-ingress-route
juju integrate qbittorrent:istio-ingress-route arr-ingress:istio-ingress-route
juju integrate sabnzbd:istio-ingress-route arr-ingress:istio-ingress-route
# Plex
juju integrate plex:istio-ingress-route plex-ingress:istio-ingress-route
# Overseerr
juju integrate overseerr:istio-ingress-route overseerr-ingress:istio-ingress-route
Connect Service Mesh¶
Requires Beacon. Enables mTLS and authorization policies.
juju integrate radarr:service-mesh beacon:service-mesh
juju integrate sonarr:service-mesh beacon:service-mesh
juju integrate prowlarr:service-mesh beacon:service-mesh
juju integrate plex:service-mesh beacon:service-mesh
juju integrate overseerr:service-mesh beacon:service-mesh
juju integrate qbittorrent:service-mesh beacon:service-mesh
juju integrate sabnzbd:service-mesh beacon:service-mesh
juju integrate flaresolverr:service-mesh beacon:service-mesh
Managing Apps¶
Remove a relation:
Remove an app:
For the full list of commands, see the Juju CLI reference.
Charm Configuration Reference¶
| Charm | Charmhub |
|---|---|
| charmarr-storage-k8s | configurations |
| gluetun-k8s | configurations |
| qbittorrent-k8s | configurations |
| sabnzbd-k8s | configurations |
| radarr-k8s | configurations |
| sonarr-k8s | configurations |
| prowlarr-k8s | configurations |
| flaresolverr-k8s | configurations |
| plex-k8s | configurations |
| overseerr-k8s | configurations |