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.
Deploy with ./install.sh. 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.
Gitea CI/CD bootstrap
After initializing this directory as its own repository and creating the Gitea repository, add the remote and perform the first push. Then run:
./create-ci-kubeconfig.sh
Store the single output line as KUBE_CONFIG_BASE64. The CI identity can only
update the already-created Plex resources named in ci-deployer.yaml; it
cannot read Secrets, run migration Jobs, or alter other workloads. Pull
requests validate, while pushes to main validate and deploy the pinned
upstream images.