# Jellyfin on Kubernetes This project runs the existing Jellyfin server as one high-performance Pod on `arschrock` in its dedicated `jellyfin` namespace. It is intentionally not highly available and cannot fail over to an ODROID. CPU and memory have scheduling requests but no limits. The 500 TB media tree remains on the host and is mounted read-only at its existing `/komposthaufen/multimedia` path. The Pod requires the existing `Videos` directory and verifies that the path is backed by bcachefs before the server starts. 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. The retained 80 GiB `jellyfin-config` RBD PVC contains the migrated Arch data, configuration, metadata, preview images, databases, and logs. Cache and transcode activity use node-local ephemeral storage. The official Jellyfin image is pinned to version 10.11.11, matching the source installation. ## One-time migration `migrate.sh` stops the Arch service, confirms no Jellyfin process remains, checks every source SQLite database, creates the PVC, and starts a one-shot copy Job: ```sh ./migrate.sh kubectl -n jellyfin logs -f job/jellyfin-archlinux-migration kubectl -n jellyfin wait --for=condition=complete \ job/jellyfin-archlinux-migration --timeout=12h ``` The Job resumes an interrupted copy using rsync, checks all copied files with checksums, and then 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 Arch source trees until the Kubernetes server has been verified and a separate backup exists. Deploy with `./install.sh`. Jellyfin remains available at `https://jellyfin.brunner.ninja` and directly on `192.168.0.2:8096`. ## Monitoring The init container enables Jellyfin's native Prometheus endpoint. The `ServiceMonitor` scrapes it internally, while the higher-priority Traefik route restricts public access to `/metrics`. Alerts cover scrape availability, restart loops, and PVC capacity. Existing 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: ```sh ./create-ci-kubeconfig.sh ``` Store the single output line as `KUBE_CONFIG_BASE64`. The CI identity can only update the already-created Jellyfin 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 image.