Install and Configure Multi-tenant Scaling
This guide installs isolated KEDA operators and optional Kedify Pod Autoscaler (KPA) controllers inside one Kubernetes cluster. Each pair owns a non-overlapping set of namespaces. Workloads can use native HPA through the shared metrics adapter or KPA for a tenant-scoped 1 ↔ N control loop.
For the architecture and isolation model without the installation steps, see Multi-tenant KEDA & KPA.
Plan the deployment
Section titled “Plan the deployment”Choose whether tenants will use the shared native HPA path or add KPA for a tenant-scoped 1 ↔ N control loop. A cluster can use both paths for different ScaledObjects.
The KEDA Helm chart supports three modes through kedify.multitenant.mode:
| Mode | Purpose | Shared metrics adapter, webhooks, and CRDs |
|---|---|---|
"" | Standard single-tenant installation | Installed normally |
"default" | Default operator and fallback route for a multi-tenant installation | Installed once |
"tenant" | Additional tenant operator | Reuses the default installation’s shared components |
Before installing, decide the release name and watchNamespace set for every operator. Apply these rules:
- Install exactly one KEDA release in
defaultmode. - Give every additional KEDA release a unique name and use
tenantmode. - Keep tenant
watchNamespacesets non-overlapping. - If a tenant uses KPA, give its KPA controller exactly the same watch set as its KEDA operator.
- Set
crds.install=trueon one KPA release only; usefalseon every additional KPA release. - Keep
kedify.kpa.defaultClass=hpauntil a low-risk KPA canary is healthy.
The commands below place the control-plane releases in keda, but you can use separate installation namespaces. See Multi-tenant KEDA & KPA for routing, registration, mTLS, and isolation details.
Install multi-tenant scaling
Section titled “Install multi-tenant scaling”Prerequisites
Section titled “Prerequisites”- A Kubernetes cluster
- Helm 3
- A Kedify subscription (helm installation guide)
- Kubernetes 1.33 through 1.35 if you plan to enable KPA
Add the Kedify chart repository:
helm repo add kedifykeda https://kedify.github.io/chartshelm repo updateReplace the version placeholders below with compatible released chart versions.
1. Install the kedify-agent with multitenancy enabled
Section titled “1. Install the kedify-agent with multitenancy enabled”helm upgrade --install kedify-agent kedifykeda/kedify-agent \ --namespace keda --create-namespace \ --set agent.features.multitenantKEDAEnabled=true \ --set agent.features.kedifyPodAutoscalerEnabled=true \ --set agent.orgId="<your-org-id>" \ --set agent.apiKey="<your-api-key>"The Agent watches tenant registrations, synchronizes TLS certificates to the metrics adapter, and enables KPA telemetry and Dashboard support. Upgrade the Agent chart and image together: the chart derives the CRD metadata, Lease, ReplicaSet, and tenant log permissions from these existing feature flags, so no additional RBAC values are required. For an HPA-only topology, omit agent.features.kedifyPodAutoscalerEnabled=true and skip the KPA-specific steps below.
2. Install the default KEDA operator in multi-tenant mode
Section titled “2. Install the default KEDA operator in multi-tenant mode”helm upgrade --install keda kedifykeda/keda \ --version <KEDA_VERSION> \ --namespace keda --create-namespace \ --set kedify.multitenant.mode=default \ --set-string watchNamespace=keda \ --set kedify.kpa.enabled=false \ --set kedify.kpa.defaultClass=hpaThis installs the full KEDA stack with the multitenant routing layer enabled and keeps workloads on HPA while KPA starts. watchNamespace=keda scopes this operator to the keda namespace. Namespaces not matched by any tenant still fall back to this default tenant for HPA metric routing. To also reconcile ScaledObjects in unassigned namespaces, omit watchNamespace to watch cluster-wide.
3. Optionally install the default KPA controller
Section titled “3. Optionally install the default KPA controller”Install KPA with the same namespace watch set. Exactly one KPA release installs the cluster-scoped CRD:
helm upgrade --install kpa oci://ghcr.io/kedify/charts/kpa \ --version <KPA_VERSION> \ --namespace keda \ --set crds.install=true \ --set-string watchNamespace=kedaEnable KPA in the default KEDA release after both controllers are ready. Keep HPA as the default until a canary workload has been verified:
helm upgrade keda kedifykeda/keda \ --namespace keda \ --reuse-values \ --set kedify.kpa.enabled=true \ --set kedify.kpa.defaultClass=hpa \ --set kedify.kpa.deploymentName=kpa4. Deploy tenant KEDA operators and optional KPA controllers
Section titled “4. Deploy tenant KEDA operators and optional KPA controllers”For each tenant, install a KEDA operator in tenant mode pointed at its namespace. Tenant mode turns off the shared singletons (CRDs, metrics server, webhooks) automatically, and the operator/service-account/certificate names are derived from the release name, so a minimal install is all you need:
helm upgrade --install foo kedifykeda/keda \ --version <KEDA_VERSION> \ --namespace keda \ --set kedify.multitenant.mode=tenant \ --set-string watchNamespace=foo \ --set kedify.kpa.enabled=false \ --set kedify.kpa.defaultClass=hpaIf the tenant will use KPA, pair it with a KPA release that watches the same namespace and connects to that tenant’s KEDA Service and certificate Secret:
helm upgrade --install kpa-foo oci://ghcr.io/kedify/charts/kpa \ --version <KPA_VERSION> \ --namespace keda \ --set crds.install=false \ --set-string watchNamespace=foo \ --set keda.metricsAddress=keda-operator-foo.keda.svc:9666 \ --set keda.metricsAuthority=keda-operator-foo.keda.svc \ --set keda.certificateSecret=kedaorg-certs-foo
helm upgrade foo kedifykeda/keda \ --namespace keda \ --reuse-values \ --set kedify.kpa.enabled=true \ --set kedify.kpa.defaultClass=hpa \ --set kedify.kpa.deploymentName=kpa-fookedify.kpa.deploymentName is required for a multi-tenant KPA-enabled KEDA release. It gives the Agent an exact Deployment identity instead of relying on workload-controlled labels. The Agent records that Deployment’s Kubernetes UID and checks it again before collecting KPA metrics or logs.
Repeat for each tenant with a unique release name and non-overlapping watchNamespace. Keep kedify.kpa.defaultClass aligned across the default and tenant KEDA releases if ScaledObjects will rely on the installation default. See the KPA feature guide for class selection, evaluation intervals, supported metrics, and limitations.
5. Create ScaledObjects
Section titled “5. Create ScaledObjects”Create ScaledObjects in tenant namespaces as usual; they are picked up by the correct operator automatically. Add autoscaling.kedify.io/class: kpa to move a canary to the tenant’s KPA controller without changing the ScaledObject spec.
apiVersion: keda.sh/v1alpha1kind: ScaledObjectmetadata: name: my-scaledobject namespace: foo annotations: autoscaling.kedify.io/class: kpaspec: scaleTargetRef: name: my-deployment triggers: - type: prometheus metadata: serverAddress: http://prometheus.monitoring.svc:9090 query: sum(rate(http_requests_total{namespace="foo"}[2m])) threshold: "100"Remove the annotation to follow kedify.kpa.defaultClass, or set it to hpa to explicitly select native HPA. To override the KPA evaluation interval for this ScaledObject, add autoscaling.kedify.io/sync-period-seconds: "15". Keep HPA as the default until the canary has scaled out and back in successfully.
Helm values reference
Section titled “Helm values reference”KEDA Chart (kedifykeda/keda)
Section titled “KEDA Chart (kedifykeda/keda)”| Value | Default | Description |
|---|---|---|
kedify.multitenant.mode | "" | Deployment mode: "" (disabled), "default", or "tenant" |
watchNamespace | "" | Namespace(s) this operator watches (empty = cluster-wide) |
kedify.multitenant.agentNamespace | keda | Namespace where kedify-agent runs |
kedify.multitenant.agentServiceAccount | kedify-agent | ServiceAccount of kedify-agent |
kedify.kpa.enabled | false | Allow KEDA to generate KPA for selected ScaledObjects |
kedify.kpa.defaultClass | hpa | Autoscaler class when a ScaledObject has no class annotation |
kedify.kpa.deploymentName | "" | Exact KPA Deployment name; required with multitenant KPA |
In tenant mode the chart turns off the shared singletons (metricsServer, webhooks, crds) and derives the operator, service account, and certificate names from the release name automatically, so those values don’t need to be set.
Kedify Agent Chart (kedifykeda/kedify-agent)
Section titled “Kedify Agent Chart (kedifykeda/kedify-agent)”| Value | Default | Description |
|---|---|---|
agent.features.multitenantKEDAEnabled | false | Enable multi-tenant KEDA discovery and certificate sync |
agent.features.kedifyPodAutoscalerEnabled | false | Enable KPA telemetry and Dashboard support |
KPA Chart (oci://ghcr.io/kedify/charts/kpa)
Section titled “KPA Chart (oci://ghcr.io/kedify/charts/kpa)”| Value | Default | Description |
|---|---|---|
crds.install | true | Install the cluster-scoped KPA CRD from one designated release |
watchNamespace | "" | Namespaces owned by this controller (empty = cluster-wide) |
keda.metricsAddress | keda-operator.keda.svc:9666 | gRPC address of the paired KEDA operator |
keda.metricsAuthority | keda-operator.keda.svc | TLS authority of the paired KEDA operator |
keda.certificateSecret | kedaorg-certs | Secret containing the paired KEDA client certificates |
Verify the installation
Section titled “Verify the installation”Wait for the default controllers and every tenant pair, then confirm that each workload has exactly one generated horizontal autoscaler. Repeat the rollout checks with each tenant Deployment name, such as keda-operator-foo and kpa-foo:
kubectl rollout status deployment/keda-operator -n kedakubectl rollout status deployment/kpa -n kedakubectl get scaledobject,hpa,kpa -Akubectl get events -A --sort-by=.lastTimestampOn an HPA-only installation without the KPA CRD, use:
kubectl rollout status deployment/keda-operator -n kedakubectl get scaledobject,hpa -AFor a KPA canary, confirm that the target has one KPA and no HPA before applying production load.
Status and troubleshooting
Section titled “Status and troubleshooting”The Kedify Agent reports multi-tenant status in the KedifyConfiguration custom resource:
kubectl get kedifyconfiguration -n keda -o yamlCheck status.discoveredTenants[] for per-tenant health:
| Field | Description |
|---|---|
name | Installation identifier in namespace/release form |
namespace | Namespace containing the tenant KEDA control plane |
operatorDeploymentName | Registered KEDA operator Deployment |
watchNamespace | Comma-separated workload namespaces owned by this installation |
address | Tenant KEDA MetricsService gRPC address |
isDefault | Whether this is the default tenant installation |
operatorReady | Whether the KEDA operator Deployment is ready |
tlsCertReady | Whether the TLS certificate Secret is available |
configSynced | Whether tenant routing is synced to the shared metrics adapter |
message | Human-readable tenant diagnostic, when present |
kpa.operatorObserved | Whether the Agent inspected this tenant’s KEDA operator KPA configuration |
kpa.enabled | Whether KPA is enabled in this tenant’s KEDA operator |
kpa.deploymentName | Matched KPA controller Deployment in namespace/name form |
kpa.deploymentUID | Kubernetes UID used to authorize the registered KPA Deployment |
kpa.controllerReady | Whether all desired KPA controller replicas are available |
kpa.version | Matched KPA controller version |
kpa.watchNamespace | Namespace set observed on the KPA controller |
kpa.kedaMetricsAddress | KEDA MetricsService address used by the KPA controller |
kpa.paired | Whether the KPA namespace set and MetricsService match this KEDA installation |
kpa.crdOwner | Helm release that owns the cluster-scoped KPA CRD, in namespace/release form |
kpa.message | Human-readable KPA diagnostic, when capability or pairing is incomplete |
An installation where KPA is intentionally not installed should report kpa.enabled: false, kpa.controllerReady: false, and kpa.paired: false. This is distinct from kpa.operatorObserved: false, which means the Agent could not inspect the tenant KEDA operator.
To check the metrics adapter’s tenant connections:
# Check adapter logs for tenant routing eventskubectl logs -n keda deploy/keda-operator-metrics-apiserver | grep -i tenant
# Check Kubernetes events for connection statekubectl get events -n keda --field-selector reason=KedifyTenantConnectionReadykubectl get events -n keda --field-selector reason=KedifyTenantConnectionNotReadyIf a KPA cannot fetch an external metric, verify that its watch set matches the paired KEDA operator and that keda.metricsAddress, keda.metricsAuthority, and keda.certificateSecret identify that operator. For CPU or memory, verify the resource metrics path with kubectl top pods -n <tenant-namespace>.
Roll back KPA to HPA
Section titled “Roll back KPA to HPA”Keep KPA running until every selected workload has returned to a healthy HPA:
- Set
kedify.kpa.defaultClass=hpaon the default KEDA release and every tenant release. - Set
autoscaling.kedify.io/class: hpaon every ScaledObject that explicitly selects KPA. - Wait for KEDA to remove the KPAs and create healthy replacement HPAs.
- Set
kedify.kpa.enabled=false, then remove the tenant KPA controller releases. - Remove the cluster-scoped KPA CRD last, only after no KPA objects or tenant KPA installations remain. Helm retains the CRD on uninstall by default.