Skip to content

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.

juju deploy charmarr-storage-k8s --trust --channel=1/stable storage

Configure based on your storage backend:

Hostpath (recommended for single-node):

juju config storage backend-type=hostpath hostpath=/path/to/media

Native NFS (recommended for multi-node):

juju config storage backend-type=native-nfs nfs-server=192.168.1.100 nfs-path=/export/charmarr

StorageClass (CSI driver):

juju config storage backend-type=storage-class storage-class=your-storage-class size=1Ti

File Ownership (Hostpath & NFS)

For hostpath and NFS backends, the storage path must be owned by UID/GID 1000:1000 by default:

sudo chown -R 1000:1000 /path/to/your/media

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:

juju deploy gluetun-k8s --trust --channel=1/stable gluetun

Create and grant the VPN secret (key is encrypted at rest):

juju add-secret vpn-key private-key="your-wireguard-private-key"
juju grant-secret vpn-key gluetun

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):

juju config plex hardware-transcoding=true

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:

juju config qbittorrent unsafe-mode=true
juju config sabnzbd unsafe-mode=true

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.

juju integrate prowlarr:flaresolverr flaresolverr:flaresolverr

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):

juju config radarr ingress-path=/movies
juju config qbittorrent ingress-path=/

The ingress listener port defaults to 80. To change it:

juju config radarr ingress-port=8080

Deploy Istio

Control Plane:

juju deploy istio-k8s --trust --channel=2/stable istio

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.

juju deploy istio-beacon-k8s --trust --channel=2/stable beacon

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:

juju remove-relation radarr:download-client qbittorrent:download-client

Remove an app:

juju remove-application qbittorrent

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