Deploying & operating zaentrum
zaentrum is a neutral, self-hosted media platform — a catalog, an event-driven media pipeline (scan → enrich → analyze → transcode → package), and clean web / mobile / TV clients — for a library you own and are entitled to stream.
The whole platform (~16 services) ships as public container images at
ghcr.io/zaentrum/<service> and is operator-managed: the
zaentrum-operator renders everything from one canonical Helm
chart (operator/platform/chart, embedded in the operator image) driven by a
single Zaentrum custom resource. Chart values map 1:1 onto the CR spec.
Pick your path
Section titled “Pick your path”| You want to… | Start here |
|---|---|
| Try it in one command (a whole cluster in one container) | Self-hosting → Appliance |
| Run your own instance on Kubernetes | Self-hosting |
Understand / reference every Zaentrum CR field | Operator & CR reference |
| Reproduce the public reference demo (OKD + GitLab CI GitOps) | Reference demo |
| Ship a change to a running platform (day-2) | Updating |
| Check a change on a running instance (end-to-end tests against the demo, read-only) | e2e/ |
| Update the operator's own controller (OLM, install manifest, appliance) | Updating the operator |
| Pick a release channel, install a release, or cut one | Releases |
| Fix a broken deploy | Troubleshooting |
| Extend it with an addon (or write one) | Extending zaentrum |
| Understand how it fits together | Architecture · Decision records |
| Understand how the library is kept on storage (manifest, metadata, versions) | The library format |
| See how the portal behaves (the home page, settings and setup) | The portal |
| See how the chino apps behave (title actions, playback, subtitles, downloads, casting, words) | The chino apps |
Start with Prerequisites regardless of path.
The deployment model — four layers by actor and cadence
Section titled “The deployment model — four layers by actor and cadence”Every deploy topology is the same four layers; only who runs each layer and
how often changes. Confusing these is the usual source of pain (e.g. trying to
create a cluster-scoped PersistentVolume from the CI service account).
flowchart TD
P["Layer 0 · Prerequisites<br/>cluster · NFS/storage · GPU+driver · DNS/TLS · images<br/><i>cluster owner, once</i>"]
B["Layer 1 · One-time bootstrap (cluster-admin)<br/>install operator · namespace · PVs · RBAC · CI vars<br/><i>cluster-admin, once per cluster</i>"]
D["Layer 2 · Deploy (repeatable)<br/>apply the Zaentrum CR → operator renders the platform<br/><i>GitOps / CI, every release</i>"]
V["Layer 3 · Verify<br/>CR Ready · pods · topics · seed/scan · playback"]
U["Layer 4 · Day-2 updates<br/>app image · chart/operator roll · CR value change"]
P --> B --> D --> V --> U
U -. re-deploy .-> D
- Layer 0 — Prerequisites. The cluster + external dependencies. The appliance needs almost none of it; a real cluster needs storage, (optionally) a GPU node, and DNS/TLS.
- Layer 1 — one-time bootstrap. The cluster-scoped, privileged bits that a
deploy pipeline cannot do itself: install the operator (CRD + ClusterRoles),
create the namespace +
PersistentVolumes, grant RBAC, set CI credentials. See Reference demo → bootstrap. - Layer 2 — deploy. Apply a
ZaentrumCR; the operator reconciles the whole platform via server-side apply. This is the only step that repeats per release. - Layer 3 — verify.
oc get zaentrum→Phase Ready, then a smoke test. - Layer 4 — updates. Shipping a new app image, a chart change (needs an operator roll), or a CR value change.
The three topologies
Section titled “The three topologies”| Topology | What it is | Deploy path |
|---|---|---|
| Appliance | One --privileged container = a single-node k3s that installs the operator, which brings up the platform. linux/amd64, port 80. Zero-clone. | self-hosting.md#a-one-command-appliance |
| Self-host on k8s | Install the operator, apply a CR (or helm install the chart). The older k3s / Compose profiles are not supported today. | self-hosting.md |
| Reference demo | The public demo at zaentrum.demo.nalet.cloud on OKD, deployed by GitLab CI. The worked example of a real GitOps deploy. | reference-demo.md |
The media pipeline is event-driven (Kafka)
Section titled “The media pipeline is event-driven (Kafka)”Stage handoffs are Kafka events keyed by item_id
(stube.catalog.item.{discovered,enriched,analyzed,transcoded}); the DB
processing-step rows record state while events drive the handoff. A bundled
single-node KRaft broker carries it by default (features.kafka), or point at an
external cluster. If you want topics to survive a broker restart, back the
broker with a volume via storage.kafkaPvc (see the
CR reference and Troubleshooting).