Deploy on OpenShift
This guide deploys Zaris on Red Hat OpenShift (OCP 4.x — ARO, ROSA, or self-managed) with the official Helm chart, then reaches the Web Console over HTTPS the OpenShift way. OpenShift diverges from vanilla Kubernetes in two places this guide covers carefully: external traffic comes in through Routes (not Ingress/LoadBalancer), and pods run under SecurityContextConstraints (SCCs) that pin them to a non-root, arbitrary UID.
This is a platform-specific walkthrough. For how the chart works — the StatefulSet model, the connection-string client, scaling, and every value — read the Kubernetes overview first. This page assumes those concepts and focuses on the OpenShift-specific steps.
Prerequisites
-
An OpenShift cluster (OCP 4.x — Azure Red Hat OpenShift, ROSA, or self-managed) that you can reach. Creating the cluster itself (ARO/ROSA install) is out of scope; this guide assumes one already exists.
-
The
ocCLI, logged in against the cluster:oc login https://api.<cluster-domain>:6443 -u <user>
oc whoami # confirm you are logged in -
Helm v3. OpenShift ships a
helmCLI you can download from the web console's Command Line Tools page, or use your own — the chart is a standard OCI Helm chart and works with either.
Create a project
An OpenShift project is a namespace with extra metadata. Create one for Zaris:
oc new-project zaris
oc new-project also switches your current context to it, so the -n zaris on later commands is belt-and-suspenders. (Cluster creation — ARO/ROSA/self-managed install — is out of scope; this only creates the project inside an existing cluster.)
SecurityContextConstraints (important)
OpenShift admits pods through SecurityContextConstraints. The default SCC for ordinary workloads, restricted-v2, runs every container as an arbitrary non-root UID from the project's assigned range, drops all Linux capabilities, and forbids privilege escalation.
The Zaris node and manager images run fine under restricted-v2 with no changes. They are plain .NET aspnet runtime images — nothing in them needs root, a fixed UID, or extra capabilities. The node's data directory is an emptyDir that the runtime user can always write, so the arbitrary UID is a non-issue. You do not need to grant a custom SCC for a normal deploy.
Deploy as usual (see below), then confirm the pods came up:
oc get pods -n zaris
Expect the node and manager pods to reach Running / Ready under the default service account.
If a pod fails to start with an SCC-related error (an fsGroup mismatch, or an image that insists on a specific UID), you can grant a more permissive SCC to the project's service account as a fallback:
oc adm policy add-scc-to-user anyuid -z default -n zaris
This is a fallback, not the recommended default — the images are designed to run under restricted-v2, so reach for anyuid only if you have a concrete SCC denial, and prefer the least-privilege SCC that unblocks it.
(Optional) Dedicated cache nodes
For production it is common to keep the cache tier on its own worker nodes so cache pods get predictable resources and never compete with application pods. On OpenShift, worker capacity is managed by MachineSets, which is cluster-admin territory — this section is deliberately briefer than the cloud guides.
List the MachineSets and either scale an existing one or clone one for the cache tier:
oc get machineset -n openshift-machine-api
On the MachineSet you dedicate to Zaris, add a label and a taint under its pod template so only Zaris lands there. Edit spec.template.spec.metadata.labels and spec.template.spec.taints:
# oc edit machineset <name> -n openshift-machine-api
spec:
template:
spec:
metadata:
labels:
workload: zaris
taints:
- key: workload
value: zaris
effect: NoSchedule
New machines from that set come up labelled workload=zaris and tainted workload=zaris:NoSchedule — the taint repels ordinary pods, and the label is what Zaris's nodeSelector targets. Pin the whole chart (nodes and manager) to those nodes with a values file:
# cache-nodes.yaml — pin the chart to the cache machine set
node:
nodeSelector:
workload: zaris
tolerations:
- key: workload
operator: Equal
value: zaris
effect: NoSchedule
manager:
nodeSelector:
workload: zaris
tolerations:
- key: workload
operator: Equal
value: zaris
effect: NoSchedule
Add -f cache-nodes.yaml to the helm install below.
node.replicas should be a multiple of node.replicationFactor so every partition has both copies on distinct pods. On 3 cache nodes you have three choices:
- RF 3 (
--set node.replicationFactor=3 --set node.replicas=3) — a copy on all three nodes; survives 2 node losses. Simplest for exactly 3 nodes. - RF 2, replicas 3 — the chart warns that one partition ends up single-copy (3 ÷ 2 doesn't divide evenly); tolerable for non-critical data, not fully durable.
- 4 cache nodes with RF 2, replicas 4 — clean 2× replication with room to lose any one node.
Deploy Zaris (private first)
Start private and unauthenticated — the safe default. The chart image is public, so the pull needs no registry login:
helm install zaris oci://registry-1.docker.io/clustron/zaris -n zaris \
--set node.replicas=4 \
--set node.replicationFactor=2
Add -f cache-nodes.yaml here if you set up dedicated cache nodes above. Wait for the node StatefulSet to roll out (a Ready pod already owns and serves its share of the data):
oc rollout status statefulset/zaris -n zaris
oc get pods -n zaris
Open the console privately with a port-forward — this needs no security because only you, through your oc session, can reach it:
oc -n zaris port-forward svc/zaris-manager-console 8080:7810
# → http://localhost:8080
A 503 for the first few seconds is normal: the console URL is readiness-gated and goes live once the store is attached and serving. The management port (7801) is never exposed — only the console port (7810) is ever published.
Console over HTTPS via a Route (the OpenShift way)
A Route is OpenShift's native way to publish a Service under a hostname, and with edge termination it gives you HTTPS with essentially zero setup — the built-in router terminates TLS at the cluster's ingress. This is the easiest trusted-HTTPS path on any platform.
1. Enable control-plane security first. Any externally-reachable console must require a login. A hand-made Route bypasses the chart's exposure guard (see the warning below), so you must turn security on before creating the Route:
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set manager.security.enabled=true \
--set manager.security.adminPassword='<change-me>'
The manager self-provisions the admin before the pod is Ready, so the URL is never live un-provisioned. The default admin/admin must not ship — set a real password (or, better, --set manager.security.existingSecret=<secret> with keys admin-username/admin-password). Keep manager.console.expose=portForward (the default): the Route targets the internal ClusterIP console Service directly, so you don't want the chart also creating a LoadBalancer or Ingress.
2. Create an edge-terminated Route to the console Service on port 7810:
oc create route edge zaris-console \
--service=zaris-manager-console --port=7810 -n zaris
Edge termination means OpenShift terminates HTTPS at the router using the cluster's wildcard ingress certificate — publicly trusted if your cluster runs a real cert, otherwise the default self-signed one (browsers will warn, fine for internal use). The router speaks plain HTTP to the console Service inside the cluster.
3. Get the URL:
oc get route zaris-console -n zaris -o jsonpath='{.spec.host}'
# → open https://<route-host> and sign in as admin / <your password>
Bring your own certificate. To serve the Route with your own trusted cert on a custom hostname, pass the PEM files at creation:
oc create route edge zaris-console \
--service=zaris-manager-console --port=7810 -n zaris \
--hostname=zaris-console.example.com \
--cert=tls.crt --key=tls.key --ca-cert=ca.crt
For its own console.expose=ingress|loadBalancer modes the chart refuses to render unless manager.security.enabled=true with an admin credential — you can't accidentally publish an unauthenticated admin console. A Route you create by hand with oc create route / oc expose does not hit that guard. So when you use a Route, you are responsible for enabling manager.security.enabled=true first (step 1). Never Route an unauthenticated console publicly.
ingress mode also worksIf your cluster runs the standard Ingress Controller, OpenShift syncs Ingress objects into Routes automatically — so the chart's manager.console.expose=ingress path works too (and it does enforce the security guard). The hand-made edge Route above is simply the more idiomatic and lower-friction OpenShift path.
Cluster TLS (node-to-node + client)
The Route TLS above secures the browser↔console hop. A separate, independent layer secures the data plane — node↔node replication and client↔node traffic — via node.tls.enabled with a cluster CA and one per-node leaf certificate mounted from a Secret.
Generating the CA and per-node leaves is covered in the security docs — follow those to produce ca.pem and a Secret of <nodeId>.pfx leaves:
- CA and trust modes — the four trust models and which to pick.
- Certificate management — generating the CA + per-node multi-SAN leaves and creating the Secret.
Create the cert Secret in the project, then enable data-plane TLS:
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set node.tls.enabled=true \
--set node.tls.certSecret=zaris-node-certs \
--set-file node.tls.caPem=ca.pem
node.tls.certSecret is a Secret whose keys are per-node <nodeId>.pfx leaves; node.tls.caPem is supplied from a local file with --set-file. Clients then connect with the zariss:// scheme and the cluster CA:
zariss://zaris-client.zaris.svc.cluster.local:7861/zaris-k8s?ca=/path/ca.pem
Connect your app
There is one store per release, named by cluster.id (default zaris-k8s) — that value is both the cluster id and the store name your clients use. In-cluster applications connect through the client Service DNS:
# plaintext
zaris://zaris-client.zaris.svc.cluster.local:7861/zaris-k8s
# TLS (when cluster TLS is enabled)
zariss://zaris-client.zaris.svc.cluster.local:7861/zaris-k8s?ca=/path/ca.pem
Replace zaris in the DNS name with your project name, and zaris-k8s with your cluster.id if you changed it. See the Kubernetes overview for the C# / ASP.NET and PowerShell client examples.
Scale
Scaling has two independent dimensions on OpenShift:
More worker capacity (room for pods) — scale the MachineSet:
oc scale machineset <name> -n openshift-machine-api --replicas=4
More Zaris pods (capacity + partitions) — scale the chart. Scaling out is lossless resharding: new pods join and the partition map grows onto them with no restart of existing pods:
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set node.replicas=6
Keep node.replicas a multiple of node.replicationFactor. Scale in one step at a time (it is not yet drain-safe) — see the scaling notes.
Tear down
Deleting the project removes the entire Zaris footprint — the StatefulSet, manager, Services, Secrets, and the Route:
oc delete project zaris
Deleting the cluster itself depends on how it was provisioned (ARO, ROSA, or self-managed) and is out of scope here — but deleting the project stops the Zaris workloads and frees the resources they held. If you dedicated a MachineSet to the cache tier, scale it back down or delete it separately in openshift-machine-api.
Next steps
- Understand the trust model behind mounted certificates → CA and trust modes.
- Day-two certificate operations → Certificate management.
- The chart in full — every value, scaling, rolling updates → Kubernetes overview.