Plex on Kubernetes

This project runs the existing Plex server as a single high-performance Pod on arschrock in its dedicated plex namespace. It is intentionally not highly available and can never fail over to an ODROID. CPU and memory have scheduling requests but no limits.

The 500 TB media tree stays on the host and is mounted read-only from /komposthaufen/multimedia. The Pod also requires the existing Videos directory and an init container verifies that the path is backed by bcachefs. If the encrypted filesystem has not been unlocked and mounted, Plex does not start. The Deployment also requires the explicit media.brunner.ninja/multimedia-ready=true node label. The shared ../media-storage-offline.sh helper removes it and stops the media Pods before unmounting; ../media-storage-online.sh validates the mount before restoring the label.

Persistent Plex configuration, metadata, preview images, and SQLite databases live on the retained 160 GiB plex-config RBD PVC. Transcodes use node-local ephemeral storage. The official Plex image is pinned to the same version as the migrated Arch installation.

One-time migration

migrate.sh stops the Arch service, confirms no Plex process remains, checks both source SQLite databases, creates the PVC, and starts a one-shot copy Job:

./migrate.sh
kubectl -n plex logs -f job/plex-archlinux-migration
kubectl -n plex wait --for=condition=complete \
  job/plex-archlinux-migration --timeout=12h

The Job refuses to overwrite a non-empty destination and compares regular-file counts and byte totals before writing its completion marker. Copy I/O is deliberately duty-cycle throttled so the migration cannot monopolize bcachefs and Ceph. Do not delete the source /var/lib/plex tree until the Kubernetes server has been verified and a separate backup exists.

The historical migration scripts remain for recovery; routine deployment is owned by Argo CD. Plex remains available at https://plex.brunner.ninja and directly on 192.168.0.2:32400.

Monitoring

The Pod contains a rootless Plex exporter. Its token is read from the migrated Preferences.xml into a memory-backed volume; it is not duplicated in a Kubernetes Secret or environment variable. A ServiceMonitor and alerts cover the exporter, Plex API access, restart loops, and PVC capacity. Existing Tautulli, node-exporter, Netdata, SMART, and bcachefs monitoring remain in use.

Helm and GitOps

This repository owns the Plex chart: Deployment, Service, media guard, exporter, ingress option, and monitoring. The chart preserves the existing Deployment selector used by media-storage-offline.sh and media-storage-online.sh. The homelab's node pin, guarded hostPath, existing PVC, public host and resource requests are selected in the GitOps values.

helm repo add brunner.ninja https://brunner.ninja/charts
helm repo update brunner.ninja
helm upgrade --install plex brunner.ninja/plex --namespace plex --create-namespace

To install this checkout manually, run ./install.sh -f my-values.yaml with the target namespace and media mount configured. Main-branch CI validates, publishes the chart using the upstream application Git tag plus git describe, then promotes its exact version to GitOps. CI needs PACKAGE_USERNAME, PACKAGE_TOKEN, and GITOPS_TOKEN, but no kubeconfig. The Plex image is the official upstream image and is selected by Chart.yaml.appVersion.

S
Description
My kubernetes configuration files to run plex in my kubernetes cluster
Readme GPL-3.0
88 KiB
Languages
Shell 100%