Deploy on EKS
This guide provisions an Amazon EKS cluster and deploys Clustron Zaris on it with the official Helm chart, from a private port-forwarded console through to a secured public console over HTTPS and full cluster TLS. Every step uses real chart values and copy-pasteable commands — for the chart internals (StatefulSet identity, discovery, scaling, connection strings), see the Kubernetes guide.
Zaris nodes are in-memory. Durability comes from the replication factor (RF), so a rescheduled pod rebuilds its share from replicas — no EBS volume or PersistentVolume is required.
Prerequisites
- An AWS account with permission to create EKS clusters, EC2 instances, and load balancers.
- The
awsCLI, configured with credentials and a default region — runaws configureand confirm withaws sts get-caller-identity. eksctl— the official EKS provisioning CLI.kubectl— the Kubernetes CLI.helmv3 — for the chart.
Create the EKS cluster
eksctl creates the control plane, a managed node group, the VPC, and wires your kubeconfig in one command:
eksctl create cluster \
--name zaris-eks \
--region us-east-1 \
--nodegroup-name apps \
--node-type m6i.xlarge \
--nodes 3
This takes ~15 minutes (EKS provisions a VPC, the managed control plane, and the EC2 node group). When it finishes, eksctl has already merged the cluster into your kubeconfig and set it as the current context, so you can talk to it immediately:
kubectl get nodes
# 3 nodes, all Ready
(Optional) Dedicated cache node group
For predictable performance, run Zaris on its own EC2 nodes, isolated from your application pods. Add a second managed node group that is labeled for Zaris and tainted so nothing else lands on it:
eksctl create nodegroup \
--cluster zaris-eks \
--region us-east-1 \
--name cache \
--node-type m6i.xlarge \
--nodes 3 \
--node-labels workload=zaris \
--node-taints workload=zaris:NoSchedule
Then pin Zaris to that group with a values file — the nodeSelector keeps Zaris on the cache nodes and the matching toleration lets it schedule past the taint. Pin the manager the same way:
# cache-pin.yaml
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
How the two directives combine:
- The taint (
workload=zaris:NoSchedule) repels every pod that lacks a matching toleration — so your ordinary application workloads never land on the cache nodes. - The nodeSelector (
workload=zaris) forces the Zaris pods onto those labeled nodes and nowhere else. - Your application workloads are unchanged — they keep scheduling onto the
appsnode group with no taint to tolerate.
Pass this file to every helm install/upgrade below with -f cache-pin.yaml.
With 3 cache nodes you have three good options: RF 3 (--set node.replicationFactor=3 --set node.replicas=3, survives losing 2 nodes); RF 2 with replicas=3 (the chart warns that replicas is not a multiple of RF, so one partition ends up single-copy); or add a 4th node for a clean RF 2 (replicas=4) where every partition is fully replicated. Keep node.replicas a multiple of node.replicationFactor for full durability.
Deploy Zaris (private first)
Start private — the default console exposure is a port-forward, which needs no security and no load balancer. Install the chart from Docker Hub's OCI registry (public pull):
kubectl create namespace zaris
helm install zaris oci://registry-1.docker.io/clustron/zaris -n zaris \
--set node.replicas=4 \
--set node.replicationFactor=2
# add -f cache-pin.yaml if you created the dedicated node group
node.replicas=4 with node.replicationFactor=2 gives 2 partitions, each replicated on distinct pods. Wait for rollout, then reach the console over a port-forward:
kubectl -n zaris rollout status statefulset/zaris
kubectl -n zaris port-forward svc/zaris-manager-console 8080:7810
# → http://localhost:8080
The store (cluster.id, default zaris-k8s) is auto-attached on boot, so it appears in the console with no manual register step. A brief 503 in the first few seconds after install is normal — the console URL is readiness-gated on the store being attached and serving.
Expose the console publicly (LoadBalancer → AWS NLB, HTTP)
To reach the console without a port-forward, switch manager.console.expose to loadBalancer. On EKS a Service of type=LoadBalancer automatically provisions an AWS load balancer — an in-tree classic/NLB on a bare cluster, or a Network Load Balancer with IP-target control if you have the AWS Load Balancer Controller installed. Either works; the in-tree LB is fine for a first deploy.
Because this publishes an admin console, the chart refuses to render it unless control-plane security is on with an admin credential:
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set manager.console.expose=loadBalancer \
--set manager.security.enabled=true \
--set manager.security.adminPassword='<change-me>' \
--set 'manager.console.loadBalancer.sourceRanges={203.0.113.10/32}'
The sourceRanges allowlist (your IP as /32) is strongly recommended — without it the LB is open to the internet. The manager seeds the admin before it becomes Ready, so the URL is never published to an un-provisioned cluster.
Get the address — on EKS the LoadBalancer surfaces as a DNS hostname, not an IP:
kubectl -n zaris get svc zaris-manager-console -w
# wait for EXTERNAL-IP to become a hostname, then:
kubectl -n zaris get svc zaris-manager-console \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
Open http://<that-hostname> and sign in with admin / your password. Note this is HTTP — an L4 load balancer with no TLS. For a browser-trusted, encrypted console, use HTTPS below.
Console over HTTPS
Two AWS paths. Path (a) is portable and recommended for a first deploy; path (b) is AWS-native.
(a) ingress-nginx + cert-manager + Let's Encrypt (recommended)
Install the NGINX ingress controller — on EKS this itself provisions an NLB:
helm install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
# find the controller's load balancer hostname:
kubectl -n ingress-nginx get svc ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
Point your DNS at that hostname — in Route 53, create an A / ALIAS record for zaris-console.example.com targeting the NLB (or a CNAME if you manage DNS elsewhere). Then install cert-manager and a Let's Encrypt issuer:
helm install cert-manager cert-manager \
--repo https://charts.jetstack.io \
--namespace cert-manager --create-namespace \
--set crds.enabled=true
# letsencrypt-prod.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: you@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
kubectl apply -f letsencrypt-prod.yaml
Now switch the console to an ingress — cert-manager solves the HTTP-01 challenge and mints an auto-renewing, browser-trusted certificate:
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set manager.console.expose=ingress \
--set manager.console.ingress.className=nginx \
--set manager.console.ingress.host=zaris-console.example.com \
--set manager.console.ingress.tls.enabled=true \
--set manager.console.ingress.tls.clusterIssuer=letsencrypt-prod \
--set manager.security.enabled=true \
--set manager.security.adminPassword='<change-me>'
Browse to https://zaris-console.example.com — no warning. TLS terminates at the ingress with a publicly-trusted cert; this is separate from Zaris's internal cluster CA.
(b) AWS-native ALB + ACM (brief)
If you prefer AWS-managed certificates, use the AWS Load Balancer Controller (it must be installed on the cluster) to drive an Application Load Balancer as the ingress, with a certificate from AWS Certificate Manager (ACM). TLS terminates at the ALB. Set manager.console.ingress.className=alb and pass the ACM cert plus scheme through the ingress annotations the controller reads:
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set manager.console.expose=ingress \
--set manager.console.ingress.className=alb \
--set manager.console.ingress.host=zaris-console.example.com \
--set 'manager.console.ingress.annotations.alb\.ingress\.kubernetes\.io/scheme=internet-facing' \
--set 'manager.console.ingress.annotations.alb\.ingress\.kubernetes\.io/certificate-arn=arn:aws:acm:us-east-1:123456789012:certificate/xxxx' \
--set 'manager.console.ingress.annotations.alb\.ingress\.kubernetes\.io/listen-ports=[{"HTTPS":443}]' \
--set manager.security.enabled=true \
--set manager.security.adminPassword='<change-me>'
This avoids Let's Encrypt entirely but requires the controller and an ACM certificate for your domain. For a first deploy, prefer path (a).
Cluster TLS (node-to-node + client)
Console TLS (above) secures the browser↔console hop. Cluster TLS is the independent layer that encrypts node↔node gossip and client↔node traffic, driven by node.tls.* with a CA plus a per-node leaf Secret. You pre-provision a cluster CA and one leaf certificate per node (each key <nodeId>.pfx), create a Secret from them, and enable 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=./certs/ca.pem
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
For generating the CA and per-node multi-SAN leaves, creating the Secret, and the full trust-model picture, see CA and trust modes and Certificate management.
Connect your app
In-cluster clients connect over the client Service DNS on port 7861, with the path segment equal to cluster.id:
zaris://zaris-client.zaris.svc.cluster.local:7861/zaris-k8s # plaintext
zariss://zaris-client.zaris.svc.cluster.local:7861/zaris-k8s?ca=/path/ca.pem # with cluster TLS
services.AddClustronZaris(
"zaris-k8s", "zaris://zaris-client.zaris.svc.cluster.local:7861/zaris-k8s");
The path (zaris-k8s) must equal the server's cluster.id. See the connection-string model for options (tokens, failover, TLS).
Scale
Two independent axes — grow the EC2 nodes, then grow the Zaris pods.
Add EC2 capacity to the cache (or apps) node group:
eksctl scale nodegroup --cluster zaris-eks --region us-east-1 --name cache --nodes 6
Scale Zaris onto that capacity — a scale-out is lossless (new pods join and the partition map grows onto them, no restart of existing pods):
helm upgrade zaris oci://registry-1.docker.io/clustron/zaris -n zaris --reuse-values \
--set node.replicas=6
Scaling in is not yet drain-safe — removing more than RF allows can drop every copy of a partition at once. Shrink one step at a time and let replication re-establish RF between steps. See Scaling.
Tear down (stop billing)
EKS charges an hourly control-plane fee plus the cost of every EC2 node in your node groups, and any load balancers keep billing too. Delete the whole cluster when you're finished:
eksctl delete cluster --name zaris-eks --region us-east-1
This removes the control plane, all node groups, and the load balancers the chart provisioned (console LB / ingress NLB / ALB). Confirm nothing lingers in the EC2 and Load Balancers consoles afterward.
Next steps
- Chart internals, scaling, TLS models, and troubleshooting → Kubernetes.
- Trust models behind cluster certificates → CA and trust modes and Certificate management.
- Review the deployment models → Deployment overview.