Helm chart that installs NVIDIA Run:ai (control plane + cluster) and KAI Scheduler
Shell
4
4 commits
updated Oct 15, 2026
Installs NVIDIA Run:ai (control plane and cluster) and the KAI Scheduler it schedules with
The chart wraps the published upstream charts and adds the glue between them: release profiles, validation, the Secrets the upstream charts expect to exist, and the cluster registration step.
| Subchart | Upstream chart | Version | Repository |
|---|---|---|---|
controlplane | control-plane | 3.0.20 | https://helm.ngc.nvidia.com/nvidia/runai-oss |
cluster | runai-cluster (bundles kai-resource-management v0.18.3 and kai-scheduler v0.18.3) | 3.0.20 | https://helm.ngc.nvidia.com/nvidia/runai-oss |
kai | kai-scheduler | v0.18.3 | oci://ghcr.io/kai-scheduler/kai-scheduler |
monitoring | kube-prometheus-stack | 76.2.0 | https://prometheus-community.github.io/helm-charts |
gpu | gpu-operator, fake-gpu-operator | v26.7.1, 0.0.62 | https://helm.ngc.nvidia.com/nvidia, oci://ghcr.io/run-ai/fake-gpu-operator |
runailib | library chart with the shared helpers |
The chart is installed once per namespace. Each release enables its components with a profile file:
| Order | Release | Namespace | Profile | Installs |
|---|---|---|---|---|
| 1 | runai-prerequisites | any (runai-prerequisites) | values.prerequisites.yaml | Prometheus Operator, NVIDIA GPU Operator (or the fake GPU operator with values.dev.yaml) |
| 2 | runai-backend | runai-backend | values.control-plane.yaml | Run:ai control plane: backend services, UI, Keycloak, PostgreSQL, Thanos, NATS |
| 3 | runai-cluster | runai | values.cluster.yaml | Run:ai cluster: operator, admission, agent, KAI Scheduler |
kai-scheduler | kai-scheduler | values.kai-scheduler.yaml | KAI Scheduler alone, for clusters without Run:ai |
The control plane and the cluster cannot share a release. The cluster's pre-install hooks call
the control-plane API with a cluster UUID and a bootstrap token, and the control plane issues
both only once it is running. scripts/register_cluster.sh registers the cluster and writes them
to a values file for release 3.
KAI Scheduler comes with release 3: runai-cluster depends on kai-resource-management, which
depends on kai-scheduler. The Run:ai operator writes KAI's Config and the
kai-resource-management node-pool controller writes one SchedulingShard per node pool. The
standalone kai-scheduler profile is for clusters that run KAI without Run:ai,
and the chart refuses to render it together with the cluster profile.
kubectl, curl, jq.Ingress with class nginx by default
(global.ingress.ingressClass, nginx or haproxy). OpenShift uses Routes.global.domain) that resolves to the ingress controller, both
from where scripts/install.sh runs and from inside the cluster.global.domain: PEM in controlplane.tls, a cert-manager issuer in
controlplane.tls.certManager, or a kubernetes.io/tls Secret runai-backend-tls created in
namespace runai-backend before the install.--skip-prerequisites when the cluster already has them.From a git checkout:
./helm_dependencies.sh
scripts/install.sh --domain runai.example.com -f my-values.yaml
my-values.yaml is passed to every release. A minimal one with a certificate for the domain:
controlplane:
tls:
certificate: |
-----BEGIN CERTIFICATE-----
...
key: |
-----BEGIN PRIVATE KEY-----
...
When the certificate is signed by a private CA, also set the CA so the cluster components trust it:
global:
customCA:
enabled: true
_runai:
customCA:
certificate: |
-----BEGIN CERTIFICATE-----
...
and pass --cacert ca.pem to scripts/install.sh for the registration calls.
scripts/install.sh --help lists the options: --skip-prerequisites, --skip-control-plane,
--skip-cluster, --dev (fake GPUs), --cluster-name, --admin-user, --cacert,
--insecure, --timeout.
The UI is at https://<global.domain>. The admin user is [email protected]
(controlplane.control-plane.tenantsManager.config.adminUsername) and its password is generated:
kubectl get secret runai-backend-gen-tenants-manager -n runai-backend \
-o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d
helm upgrade --install runai-prerequisites . -n runai-prerequisites --create-namespace \
-f values.prerequisites.yaml --wait
helm upgrade --install runai-backend . -n runai-backend --create-namespace \
-f values.control-plane.yaml -f my-values.yaml --set global.domain=runai.example.com \
--wait --wait-for-jobs --timeout 20m
scripts/register_cluster.sh --domain runai.example.com --output cluster-registration.yaml
helm upgrade --install runai-cluster . -n runai --create-namespace \
-f values.cluster.yaml -f my-values.yaml -f cluster-registration.yaml \
--wait --wait-for-jobs --timeout 20m
cluster-registration.yaml holds a bootstrap token. The control plane mints a new one on every
call, so regenerate the file before each install or upgrade of runai-cluster.
scripts/install.sh --domain runai.example.com --dev -f my-values.yaml
kubectl label node <node> run.ai/simulated-gpu-node-pool=default
values.dev.yaml replaces the NVIDIA GPU Operator with the fake GPU operator, which reports
8 Tesla-A10 GPUs on each labelled node.
helm upgrade --install kai-scheduler . -n kai-scheduler --create-namespace \
-f values.kai-scheduler.yaml --wait
KAI creates the queues default-parent-queue and default-queue on first install. Pods opt in
with schedulerName: kai-scheduler and the label kai.scheduler/queue: default-queue.
Layer values.openshift.yaml on every release. Upstream detects OpenShift with lookup, which
returns nothing under helm template, so the file states it: it sets
global.config.kubernetesDistribution: openshift (Routes and SCCs in the control plane) and
forces OpenShift mode in runai-cluster, kai-resource-management and kai-scheduler. Leave
global._runai.monitoring off: the Run:ai operator uses openshift-monitoring.
ArgoCD renders with helm template, so:
global._runai.mode: install (or upgrade): lookup returns nothing, and the chart then
renders the runai-reservation Namespace unconditionally.scripts/register_cluster.sh and
store its output in a Secret-backed values source, since it holds a bootstrap token.The Run:ai cluster takes its routing settings through clusterConfig, a merge patch into its
RunaiConfig. To use HAProxy instead of nginx:
cluster:
runai-cluster:
clusterConfig:
global:
ingress:
ingressClass: haproxy
NVIDIA also publishes the commercial Run:ai line (2.x) as control-plane and runai-cluster in
https://helm.ngc.nvidia.com/nvidia/runai, which needs an NGC API key. Those charts take the
same install values (controlPlane.url, controlPlane.clientSecret, cluster.uid,
cluster.url, clusterConfig). Their runai-cluster 2.26.50 schedules with the proprietary
runai-engine (v3.41.19, from nvcr.io/artifactory) instead of KAI Scheduler. This chart wraps the
open-source 3.x line from nvidia/runai-oss.
Each subchart passes its upstream chart's values one level down, for example
controlplane.control-plane.keycloakx, cluster.runai-cluster.kai-resource-management or
monitoring.kube-prometheus-stack.grafana. The upstream charts also read these globals:
| Key | Read by | Purpose |
|---|---|---|
global.domain | control plane | FQDN of the control plane |
global.ingress.ingressClass | control plane | nginx (default) or haproxy |
global.config.kubernetesDistribution | control plane | kubernetes or openshift |
global.image.registry | control plane, cluster | Registry for Run:ai images (default nvcr.io/nvidia/runai-oss) |
global.customCA.enabled | control plane, cluster | Trust the CA in Secret runai-ca-cert |
global.imagePullSecrets | control plane, KAI | Pull secrets as [{name: ...}] |
global.additionalImagePullSecrets | cluster | Pull secrets as a list of names |
| Key | Type | Default | Description |
|---|---|---|---|
| cluster | object | {} | Cluster subchart: the upstream values under cluster.runai-cluster, including the registration values and KAI under cluster.runai-cluster.kai-resource-management.kai-scheduler. See charts/cluster/values.yaml. |
| controlplane | object | {} | Control-plane subchart: TLS for global.domain, and the upstream values under controlplane.control-plane. See charts/controlplane/values.yaml. |
| global._runai.cluster.enabled | bool | false | Run:ai cluster, including KAI Scheduler. Release namespace must be runai. Needs a running control plane and the values scripts/register_cluster.sh writes. |
| global._runai.controlPlane.enabled | bool | false | Run:ai control plane. Release namespace must be runai-backend. |
| global._runai.customCA.certificate | string | "" | PEM CA bundle rendered into the runai-ca-cert Secret of the release namespace when global.customCA.enabled is true. Leave empty to create runai-ca-cert yourself. |
| global._runai.gpu.enabled | bool | false | NVIDIA GPU Operator, or the fake GPU operator (gpu.fake-gpu-operator.enabled). |
| global._runai.kaiScheduler.enabled | bool | false | Standalone KAI Scheduler, without Run:ai. Upstream documents namespace kai-scheduler. Never together with cluster, which bundles KAI. |
| global._runai.mode | string | "auto" | "auto" lets templates call lookup. Set "install" or "upgrade" when rendering with helm template (ArgoCD), where lookup returns nothing. |
| global._runai.monitoring.enabled | bool | false | kube-prometheus-stack. The Run:ai cluster requires a Prometheus Operator. |
| global._runai.registry.password | string | "" | Registry password or token. |
| global._runai.registry.server | string | "" | Registry host for the control-plane pull secret runai-reg-creds. Nothing is rendered unless server, username and password are all set. The images on nvcr.io/nvidia/runai-oss are public. |
| global._runai.registry.username | string | "" | Registry user. |
| global.customCA.enabled | bool | false | Trust the CA in Secret runai-ca-cert (key runai-ca.pem). Needed when the control-plane certificate is not signed by a public CA. |
| global.domain | string | "" | FQDN the control plane is served on. Required by the controlPlane component. |
| global.imagePullSecrets | list | [] | Pull secrets for control-plane and KAI pods, as [{name: }]. The Run:ai cluster reads global.additionalImagePullSecrets (a list of names) instead. |
| gpu | object | {} | GPU subchart: gpu.gpu-operator and gpu.fake-gpu-operator, each with an enabled switch. |
| kai | object | {} | Standalone KAI subchart: the upstream values under kai.kai-scheduler. |
| monitoring | object | {} | Monitoring subchart: the upstream values under monitoring.kube-prometheus-stack. |
Helm chart that installs NVIDIA Run:ai (control plane + cluster) and KAI Scheduler
Shell
4
4 commits
updated Oct 15, 2026
Installs NVIDIA Run:ai (control plane and cluster) and the KAI Scheduler it schedules with
The chart wraps the published upstream charts and adds the glue between them: release profiles, validation, the Secrets the upstream charts expect to exist, and the cluster registration step.
| Subchart | Upstream chart | Version | Repository |
|---|---|---|---|
controlplane | control-plane | 3.0.20 | https://helm.ngc.nvidia.com/nvidia/runai-oss |
cluster | runai-cluster (bundles kai-resource-management v0.18.3 and kai-scheduler v0.18.3) | 3.0.20 | https://helm.ngc.nvidia.com/nvidia/runai-oss |
kai | kai-scheduler | v0.18.3 | oci://ghcr.io/kai-scheduler/kai-scheduler |
monitoring | kube-prometheus-stack | 76.2.0 | https://prometheus-community.github.io/helm-charts |
gpu | gpu-operator, fake-gpu-operator | v26.7.1, 0.0.62 | https://helm.ngc.nvidia.com/nvidia, oci://ghcr.io/run-ai/fake-gpu-operator |
runailib | library chart with the shared helpers |
The chart is installed once per namespace. Each release enables its components with a profile file:
| Order | Release | Namespace | Profile | Installs |
|---|---|---|---|---|
| 1 | runai-prerequisites | any (runai-prerequisites) | values.prerequisites.yaml | Prometheus Operator, NVIDIA GPU Operator (or the fake GPU operator with values.dev.yaml) |
| 2 | runai-backend | runai-backend | values.control-plane.yaml | Run:ai control plane: backend services, UI, Keycloak, PostgreSQL, Thanos, NATS |
| 3 | runai-cluster | runai | values.cluster.yaml | Run:ai cluster: operator, admission, agent, KAI Scheduler |
kai-scheduler | kai-scheduler | values.kai-scheduler.yaml | KAI Scheduler alone, for clusters without Run:ai |
The control plane and the cluster cannot share a release. The cluster's pre-install hooks call
the control-plane API with a cluster UUID and a bootstrap token, and the control plane issues
both only once it is running. scripts/register_cluster.sh registers the cluster and writes them
to a values file for release 3.
KAI Scheduler comes with release 3: runai-cluster depends on kai-resource-management, which
depends on kai-scheduler. The Run:ai operator writes KAI's Config and the
kai-resource-management node-pool controller writes one SchedulingShard per node pool. The
standalone kai-scheduler profile is for clusters that run KAI without Run:ai,
and the chart refuses to render it together with the cluster profile.
kubectl, curl, jq.Ingress with class nginx by default
(global.ingress.ingressClass, nginx or haproxy). OpenShift uses Routes.global.domain) that resolves to the ingress controller, both
from where scripts/install.sh runs and from inside the cluster.global.domain: PEM in controlplane.tls, a cert-manager issuer in
controlplane.tls.certManager, or a kubernetes.io/tls Secret runai-backend-tls created in
namespace runai-backend before the install.--skip-prerequisites when the cluster already has them.From a git checkout:
./helm_dependencies.sh
scripts/install.sh --domain runai.example.com -f my-values.yaml
my-values.yaml is passed to every release. A minimal one with a certificate for the domain:
controlplane:
tls:
certificate: |
-----BEGIN CERTIFICATE-----
...
key: |
-----BEGIN PRIVATE KEY-----
...
When the certificate is signed by a private CA, also set the CA so the cluster components trust it:
global:
customCA:
enabled: true
_runai:
customCA:
certificate: |
-----BEGIN CERTIFICATE-----
...
and pass --cacert ca.pem to scripts/install.sh for the registration calls.
scripts/install.sh --help lists the options: --skip-prerequisites, --skip-control-plane,
--skip-cluster, --dev (fake GPUs), --cluster-name, --admin-user, --cacert,
--insecure, --timeout.
The UI is at https://<global.domain>. The admin user is [email protected]
(controlplane.control-plane.tenantsManager.config.adminUsername) and its password is generated:
kubectl get secret runai-backend-gen-tenants-manager -n runai-backend \
-o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d
helm upgrade --install runai-prerequisites . -n runai-prerequisites --create-namespace \
-f values.prerequisites.yaml --wait
helm upgrade --install runai-backend . -n runai-backend --create-namespace \
-f values.control-plane.yaml -f my-values.yaml --set global.domain=runai.example.com \
--wait --wait-for-jobs --timeout 20m
scripts/register_cluster.sh --domain runai.example.com --output cluster-registration.yaml
helm upgrade --install runai-cluster . -n runai --create-namespace \
-f values.cluster.yaml -f my-values.yaml -f cluster-registration.yaml \
--wait --wait-for-jobs --timeout 20m
cluster-registration.yaml holds a bootstrap token. The control plane mints a new one on every
call, so regenerate the file before each install or upgrade of runai-cluster.
scripts/install.sh --domain runai.example.com --dev -f my-values.yaml
kubectl label node <node> run.ai/simulated-gpu-node-pool=default
values.dev.yaml replaces the NVIDIA GPU Operator with the fake GPU operator, which reports
8 Tesla-A10 GPUs on each labelled node.
helm upgrade --install kai-scheduler . -n kai-scheduler --create-namespace \
-f values.kai-scheduler.yaml --wait
KAI creates the queues default-parent-queue and default-queue on first install. Pods opt in
with schedulerName: kai-scheduler and the label kai.scheduler/queue: default-queue.
Layer values.openshift.yaml on every release. Upstream detects OpenShift with lookup, which
returns nothing under helm template, so the file states it: it sets
global.config.kubernetesDistribution: openshift (Routes and SCCs in the control plane) and
forces OpenShift mode in runai-cluster, kai-resource-management and kai-scheduler. Leave
global._runai.monitoring off: the Run:ai operator uses openshift-monitoring.
ArgoCD renders with helm template, so:
global._runai.mode: install (or upgrade): lookup returns nothing, and the chart then
renders the runai-reservation Namespace unconditionally.scripts/register_cluster.sh and
store its output in a Secret-backed values source, since it holds a bootstrap token.The Run:ai cluster takes its routing settings through clusterConfig, a merge patch into its
RunaiConfig. To use HAProxy instead of nginx:
cluster:
runai-cluster:
clusterConfig:
global:
ingress:
ingressClass: haproxy
NVIDIA also publishes the commercial Run:ai line (2.x) as control-plane and runai-cluster in
https://helm.ngc.nvidia.com/nvidia/runai, which needs an NGC API key. Those charts take the
same install values (controlPlane.url, controlPlane.clientSecret, cluster.uid,
cluster.url, clusterConfig). Their runai-cluster 2.26.50 schedules with the proprietary
runai-engine (v3.41.19, from nvcr.io/artifactory) instead of KAI Scheduler. This chart wraps the
open-source 3.x line from nvidia/runai-oss.
Each subchart passes its upstream chart's values one level down, for example
controlplane.control-plane.keycloakx, cluster.runai-cluster.kai-resource-management or
monitoring.kube-prometheus-stack.grafana. The upstream charts also read these globals:
| Key | Read by | Purpose |
|---|---|---|
global.domain | control plane | FQDN of the control plane |
global.ingress.ingressClass | control plane | nginx (default) or haproxy |
global.config.kubernetesDistribution | control plane | kubernetes or openshift |
global.image.registry | control plane, cluster | Registry for Run:ai images (default nvcr.io/nvidia/runai-oss) |
global.customCA.enabled | control plane, cluster | Trust the CA in Secret runai-ca-cert |
global.imagePullSecrets | control plane, KAI | Pull secrets as [{name: ...}] |
global.additionalImagePullSecrets | cluster | Pull secrets as a list of names |
| Key | Type | Default | Description |
|---|---|---|---|
| cluster | object | {} | Cluster subchart: the upstream values under cluster.runai-cluster, including the registration values and KAI under cluster.runai-cluster.kai-resource-management.kai-scheduler. See charts/cluster/values.yaml. |
| controlplane | object | {} | Control-plane subchart: TLS for global.domain, and the upstream values under controlplane.control-plane. See charts/controlplane/values.yaml. |
| global._runai.cluster.enabled | bool | false | Run:ai cluster, including KAI Scheduler. Release namespace must be runai. Needs a running control plane and the values scripts/register_cluster.sh writes. |
| global._runai.controlPlane.enabled | bool | false | Run:ai control plane. Release namespace must be runai-backend. |
| global._runai.customCA.certificate | string | "" | PEM CA bundle rendered into the runai-ca-cert Secret of the release namespace when global.customCA.enabled is true. Leave empty to create runai-ca-cert yourself. |
| global._runai.gpu.enabled | bool | false | NVIDIA GPU Operator, or the fake GPU operator (gpu.fake-gpu-operator.enabled). |
| global._runai.kaiScheduler.enabled | bool | false | Standalone KAI Scheduler, without Run:ai. Upstream documents namespace kai-scheduler. Never together with cluster, which bundles KAI. |
| global._runai.mode | string | "auto" | "auto" lets templates call lookup. Set "install" or "upgrade" when rendering with helm template (ArgoCD), where lookup returns nothing. |
| global._runai.monitoring.enabled | bool | false | kube-prometheus-stack. The Run:ai cluster requires a Prometheus Operator. |
| global._runai.registry.password | string | "" | Registry password or token. |
| global._runai.registry.server | string | "" | Registry host for the control-plane pull secret runai-reg-creds. Nothing is rendered unless server, username and password are all set. The images on nvcr.io/nvidia/runai-oss are public. |
| global._runai.registry.username | string | "" | Registry user. |
| global.customCA.enabled | bool | false | Trust the CA in Secret runai-ca-cert (key runai-ca.pem). Needed when the control-plane certificate is not signed by a public CA. |
| global.domain | string | "" | FQDN the control plane is served on. Required by the controlPlane component. |
| global.imagePullSecrets | list | [] | Pull secrets for control-plane and KAI pods, as [{name: }]. The Run:ai cluster reads global.additionalImagePullSecrets (a list of names) instead. |
| gpu | object | {} | GPU subchart: gpu.gpu-operator and gpu.fake-gpu-operator, each with an enabled switch. |
| kai | object | {} | Standalone KAI subchart: the upstream values under kai.kai-scheduler. |
| monitoring | object | {} | Monitoring subchart: the upstream values under monitoring.kube-prometheus-stack. |