Skip to main content

Plex

Plex Media Server runs in the cluster as the upstream Helm chart plex-media-server, reconciled by Argo CD Application plex. The Application is multi-source: companion manifests from homelabs/plex/manifests, Helm values from $homelabs/plex/values.yaml and $private/clusters/<cluster>/overlays/plex/values.yaml, and the chart itself. The workload is a Helm StatefulSet named plex.

Access is on the home network. Public values leave the Service as ClusterIP so the shared tree stays generic; the private overlay turns it into a MetalLB LoadBalancer and sets the advertise URL. Cloudflare is not part of this path. Jellyfin on the same cluster keeps ClusterIP and uses that tunnel instead; do not copy this LoadBalancer overlay onto Application jellyfin.

Site values
#

Public homelabs/plex/values.yaml stays generic: Service type ClusterIP, the claim Secret name only, a default PLEX_SERVER_NAME, and the extra volume that mounts the media claim.

Site values belong in clusters/<cluster>/overlays/plex/values.yaml. The overlay sets the Service to LoadBalancer on port 32400, pins service.loadBalancerIP to "<loadBalancerIP>", and sets extraEnv.ADVERTISE_IP to http://<loadBalancerIP>:32400. Repeat PLEX_SERVER_NAME in the overlay so a Helm map merge cannot drop the public extraEnv key. Other site facts (domain, inventory, NAS coordinates) stay in homelabs-private/clusters/<cluster>/config/cluster.yaml or a local runbook.

Claim token
#

The Plex claim token lives in Vault at secret/homelab/plex (property PLEX_CLAIM). External Secrets materializes it as Secret plex-env in namespace plex. Tokens from https://www.plex.tv/claim/ expire in a few minutes, so write the value when you are ready to sync:

vault kv put secret/homelab/plex PLEX_CLAIM=your_claim_token

Confirm the ExternalSecret and the target Secret exist:

kubectl get externalsecret -n plex
kubectl get secret plex-env -n plex

Do not commit the token or a filled *secret*.yaml. Cluster token setup is on the Vault and External Secrets pages.

GitOps
#

Application plex already points at the public values file plus the private overlay. Sync order uses waves: hashicorp-vault, then external-secrets-operator, then plex. Register the stack once, then let Argo create and reconcile the namespace, the Longhorn config claim, the ExternalSecret, and the Helm release.

kubectl apply -k argocd
kubectl apply -f clusters/<cluster>/argocd/app-of-apps.yaml

Apply only the Plex Application if you need to register it on its own:

kubectl apply -f clusters/<cluster>/argocd/applications/plex.yaml

Check that the Application is present and healthy:

kubectl -n argocd get applications
kubectl -n argocd get application plex

Day-to-day changes go through Git and an Argo sync. The public tree no longer ships plex/deployment.yaml. Do not recreate a Deployment or Service named plex-k3s.

Storage
#

Config state lives on claim plex-config-pvc-lh (longhorn-ssd), GitOps-managed via homelabs/plex/manifests/pvc-config.yaml and Helm configExistingClaim. Media libraries stay on plex-media-pvc (nfs-csi). That media claim already exists on the cluster and is not in Git, so Argo must not recreate or prune it; there is no plex/pvc.yaml to apply. Keep the library on NFS; do not migrate media to Longhorn.