Skip to content

For the complete documentation index and AI-optimized content, see /llms.txt. All pages support markdown format via .md extension or Accept: text/markdown header.

Kedify Pod Autoscaler

For the complete documentation index and AI-optimized content, see /llms.txt. All pages support markdown format via .md extension or Accept: text/markdown header.

Kedify Pod Autoscaler (KPA) is a tenant-scoped horizontal autoscaler for the Kedify build of KEDA. KEDA continues to activate workloads and handle the 0 ↔ 1 boundary. KPA controls scaling between 1 ↔ N replicas.

Native KEDA creates Kubernetes HorizontalPodAutoscalers (HPAs), which share the cluster’s HPA controller and external-metrics API path. KPA lets each tenant operate its own autoscaling control loop beside its KEDA operator. This is useful when a large cluster needs stronger autoscaling isolation, separate tenant ownership, or independent control-plane capacity.

KPA reads external metrics from its paired KEDA operator. CPU and memory metrics continue to use Kubernetes metrics.k8s.io.

The ScaledObject remains the source of truth. Existing settings stay in the same fields, including:

  • minReplicaCount and maxReplicaCount;
  • trigger metric targets and fallback configuration; and
  • advanced.horizontalPodAutoscalerConfig, including scaling policies and stabilization windows.

KPA uses the Kubernetes autoscaling/v2 HPA spec and status shape. Do not create KPA resources manually; user-authored KPAs are not supported.

The following example installs one default multitenant KEDA/KPA pair that watches tenant-a. Replace the version placeholders with released chart versions and adjust release names, namespaces, and watchNamespace for your environment.

First install or upgrade KEDA with KPA disabled. This keeps existing workloads on HPA while KEDA creates its MetricsService and TLS Secret:

Terminal window
helm upgrade --install keda kedifykeda/keda \
--version <KEDA_VERSION> \
--namespace keda --create-namespace \
--set kedify.multitenant.mode=default \
--set-string watchNamespace=tenant-a \
--set kedify.kpa.enabled=false \
--set kedify.kpa.defaultClass=hpa

Install the matching KPA release with the same namespace watch set. Exactly one designated KPA release should install the cluster-scoped CRD:

Terminal window
helm upgrade --install kpa oci://ghcr.io/kedify/charts/kpa \
--version <KPA_VERSION> \
--namespace keda \
--set crds.install=true \
--set-string watchNamespace=tenant-a

Wait for both controllers, then enable KPA in KEDA while keeping HPA as the default for the initial canary:

Terminal window
kubectl rollout status deployment/keda-operator -n keda
kubectl rollout status deployment/kpa-controller -n keda
helm upgrade keda kedifykeda/keda \
--version <KEDA_VERSION> \
--namespace keda \
--reuse-values \
--set kedify.kpa.enabled=true \
--set kedify.kpa.defaultClass=hpa

If the Kedify Agent is installed, enable its KPA integration for Dashboard access and telemetry:

Terminal window
helm upgrade kedify-agent kedifykeda/kedify-agent \
--version <AGENT_VERSION> \
--namespace keda \
--reuse-values \
--set agent.features.kedifyPodAutoscalerEnabled=true

For a new Agent installation, add agent.features.kedifyPodAutoscalerEnabled=true to the installation command supplied by the Kedify Dashboard.

Canary one low-risk ScaledObject before changing kedify.kpa.defaultClass to kpa. Confirm that the workload has one KPA, no HPA targets the same workload, and scaling and events are healthy.

For multiple tenants, repeat the KEDA/KPA installation with distinct release namespaces and non-overlapping watchNamespace values. Set crds.install=false on every additional KPA release. The designated default KEDA release owns the cluster-wide admission webhook; keep KPA enabled there and keep its defaultClass aligned with tenant releases whenever ScaledObjects use the installation default. See Multitenant KEDA for the tenant installation model.

Select KPA without changing the ScaledObject spec:

metadata:
annotations:
autoscaling.kedify.io/class: kpa

The annotation overrides the installation default. Remove it to follow kedify.kpa.defaultClass, or set it to hpa to select native HPA explicitly. KEDA manages the transition and prevents HPA and KPA from controlling the workload at the same time.

ScalingGroup members use native HPA. If an explicitly KPA-selected ScaledObject joins a group, Kedify changes its class to hpa and emits an AutoscalingClassFallback event. That HPA selection remains after it leaves the group; select kpa again to opt back in. A ScaledObject that used KPA only through the installation default returns to that default after leaving the group.

Override the controller-wide interval for one ScaledObject:

metadata:
annotations:
autoscaling.kedify.io/class: kpa
autoscaling.kedify.io/sync-period-seconds: "15"

The value is an integer from 1 through 3600 seconds and cannot be lower than the KPA installation’s minimum interval, which defaults to five seconds. Without the annotation, KPA uses its controller-wide sync period, which defaults to 15 seconds.

This setting controls how often KPA evaluates the autoscaler. It is separate from ScaledObject pollingInterval, which controls KEDA trigger polling and affects activation from zero.

The Dashboard exposes Pod autoscaler class and KPA evaluation interval under Advanced configuration. The ScaledObject list can display and filter the selected class, and View pod autoscaler opens the generated HPA or KPA.

The Dashboard verifies that KPA is available in the selected namespace before accepting a new KPA selection. KEDA admission performs the final validation. Existing KPA selections are preserved during a temporary availability or permission failure.

Metric sourceKPA backend
ExternalPaired KEDA operator
ResourceKubernetes metrics.k8s.io
ContainerResourceKubernetes metrics.k8s.io

Object and Pods metric sources are not supported. KPA does not independently scale to zero; KEDA continues to own activation, cooldown, and the 0 ↔ 1 transition.

The KPA controller exposes /healthz, /readyz, and /metrics. The Helm chart enables scrape annotations by default. Set both serviceMonitor.enabled=true and prometheusRule.enabled=true to install the supplied ServiceMonitor and alerts.

Start troubleshooting with the ScaledObject, generated autoscaler, target, and events:

Terminal window
kubectl get scaledobject,hpa,kpa -n tenant-a
kubectl get scaledobject orders -n tenant-a -o jsonpath='{.status.hpaName}{"\n"}'
kubectl describe kpa keda-hpa-orders -n tenant-a
kubectl get events -n tenant-a --sort-by=.lastTimestamp

If external metrics fail, check the paired KEDA operator, its MetricsService and TLS Secret, and the namespace watch configuration. For CPU or memory failures, verify metrics.k8s.io with kubectl top pods -n tenant-a.

Keep KPA enabled while moving workloads back:

  1. Set kedify.kpa.defaultClass=hpa on the designated webhook release and every tenant release.
  2. Set autoscaling.kedify.io/class: hpa on every ScaledObject that explicitly selects KPA.
  3. Wait for KEDA to remove all KPAs, create replacement HPAs, and confirm those HPAs are healthy.
  4. Set kedify.kpa.enabled=false, then remove the tenant KPA controller releases.
  5. Remove the cluster-scoped KPA CRD last, only after no KPA objects or tenant installations remain. Helm retains the CRD on uninstall by default.
  • KPA is generated only from ScaledObjects; manually managed KPAs are unsupported.
  • ScalingGroup members use native HPA.
  • Tenant namespace ownership cannot overlap between KPA installations.
  • Moving a live namespace between tenant controllers requires a controlled rollout.
  • KPA supports Kubernetes 1.33 through 1.35.