Blog archive

Installing KEDA on Kubernetes with Helm: Production-Ready Setup

Learn how to install and configure KEDA on Kubernetes with Helm for scalable, secure, and production-ready environments.

Published · Republished on Medium

KubernetesKedaAutoscalingAutoscale

Screenshot from Installing KEDA on Kubernetes with Helm: Production-Ready Setup

Installing the KEDA chart is straightforward. Making event-driven autoscaling safe in production is mostly about the surrounding decisions: version compatibility, metric ownership, trigger credentials, workload limits, failure behavior, observability, and rollback.

KEDA connects event sources to Kubernetes scaling. It handles activation from zero and deactivation back to zero, while Kubernetes Horizontal Pod Autoscaler handles scaling between one and the configured maximum. That boundary matters when debugging timing or replica decisions.

KEDA does not provide full high-availability support. Multiple operator replicas use leader election, leaving one active and the others on standby; multiple replicas reduce failover time but do not increase reconciliation throughput. The external metrics API also has upstream constraints. Treat extra replicas as failure-recovery capacity, not proof of end-to-end HA.

1. Record compatibility and ownership

Before installation, record:

  • Kubernetes version
  • KEDA chart and application version
  • Helm version
  • existing HPAs and autoscaling controllers
  • target workloads and event sources
  • credential mechanism for each scaler

Check the supported Kubernetes version in the documentation for the KEDA release you plan to install. Do not copy a minimum version from a different documentation release.

One workload must not be controlled by two independent HPAs. KEDA's admission webhook detects conflicting ScaledObject/HPA ownership, but migration from an existing HPA should still be planned and tested explicitly.

2. Pin and inspect the Helm chart

Add the official chart repository and inspect available releases:

bash

helm repo add kedacore https://kedacore.github.io/charts
helm repo update

helm search repo kedacore/keda --versions
helm show values kedacore/keda --version <PINNED_CHART_VERSION> > keda-default-values.yaml

Select a released chart compatible with the cluster, pin it in Git, and review the rendered resources before applying them. KEDA's chart manages CRDs for current releases, but installations upgraded from old versions may have different CRD ownership. Establish whether Helm or a separate platform process owns CRD upgrades before the first production rollout.

bash

helm template keda kedacore/keda \
  --namespace keda \
  --version <PINNED_CHART_VERSION> \
  --values keda-values.yaml > rendered-keda.yaml

Review the operator, metrics API server, admission webhook, RBAC, Pod security settings, service accounts, and CRDs in the rendered output.

3. Install KEDA

Start with a small values file that expresses only environment-specific requirements. Do not copy arbitrary replica counts, disruption budgets, node selectors, or affinity rules unless they are supported by the pinned chart and match real labels and failure domains in your cluster. Derive every chart key from the output of helm show values for the selected release.

Install with an atomic rollback on failure:

bash

helm upgrade --install keda kedacore/keda \
  --namespace keda \
  --create-namespace \
  --version <PINNED_CHART_VERSION> \
  --values keda-values.yaml \
  --atomic \
  --timeout 10m

4. Verify the control plane

Verify more than Pod phase:

bash

kubectl -n keda get deploy,pod,service
kubectl get crd | grep keda.sh
kubectl get apiservice v1beta1.external.metrics.k8s.io
kubectl get validatingwebhookconfiguration | grep keda
kubectl -n keda get events --sort-by=.lastTimestamp

The external metrics API should report as available, the webhook should have valid endpoints and certificates, and the operator logs should not show permission or scaler-connection errors.

bash

kubectl get --raw /apis/external.metrics.k8s.io/v1beta1
kubectl -n keda logs deploy/keda-operator --tail=100
kubectl -n keda logs deploy/keda-operator-metrics-apiserver --tail=100
kubectl -n keda logs deploy/keda-admission-webhooks --tail=100

Deployment names can vary by chart version. Inspect the resources first and adjust the commands rather than assuming names in a runbook.

5. Create a bounded ScaledObject

This example scales an existing Deployment named worker from zero based on a Prometheus query. Replace the server address and query with values validated in your environment.

yaml

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: worker-queue-depth
  namespace: application
spec:
  scaleTargetRef:
    name: worker
  pollingInterval: 30
  initialCooldownPeriod: 60
  cooldownPeriod: 300
  minReplicaCount: 0
  maxReplicaCount: 20
  fallback:
    failureThreshold: 3
    replicas: 2
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleUp:
          stabilizationWindowSeconds: 0
          policies:
            - type: Percent
              value: 100
              periodSeconds: 60
        scaleDown:
          stabilizationWindowSeconds: 300
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring.svc.cluster.local:9090
        query: sum(application_queue_depth)
        threshold: "10"

Important behavior:

  • pollingInterval controls KEDA polling while the workload is at zero.
  • cooldownPeriod applies to scaling back to zero.
  • HPA behavior controls scaling between one and maxReplicaCount.
  • fallback gives a defined replica count after repeated supported-scaler errors; it is not available for every trigger type.
  • maxReplicaCount must fit downstream capacity such as database connections, queue partitions, API quotas, and cluster node limits.

Do not add a separate HPA for the same Deployment. KEDA creates and owns the HPA associated with the ScaledObject.

6. Keep trigger credentials out of the ScaledObject

Use TriggerAuthentication or ClusterTriggerAuthentication with the platform's workload-identity or secret-management standard. Prefer provider-native pod/workload identity over long-lived cloud keys.

A namespaced secret reference looks like this:

yaml

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: queue-auth
  namespace: application
spec:
  secretTargetRef:
    - parameter: password
      name: queue-scaler
      key: password

The Secret should be encrypted at rest, narrowly readable, rotated, and supplied by the environment's secret system. Avoid placing credentials directly in trigger metadata or Git.

7. Test the scaling lifecycle

Inspect the generated HPA and ScaledObject conditions:

bash

kubectl -n application get scaledobject worker-queue-depth
kubectl -n application describe scaledobject worker-queue-depth
kubectl -n application get hpa
kubectl -n application describe hpa

Then exercise these cases with a controlled workload:

  1. zero demand keeps the target at the intended minimum
  2. a known event backlog activates the workload
  3. sustained demand scales toward the expected replica count
  4. demand removal respects stabilization and cooldown
  5. trigger-source failure produces the expected fallback behavior
  6. recovery from trigger failure returns to metric-based scaling
  7. maxReplicaCount protects downstream systems
  8. an invalid second ScaledObject is rejected by admission validation

Record timestamps for trigger changes, KEDA decisions, HPA changes, Pod readiness, and completed work. This distinguishes control-plane delay from slow application startup.

8. Observability and alerts

Monitor:

  • ScaledObject READY, ACTIVE, and fallback conditions
  • operator and metrics-server reconciliation errors
  • external metrics API availability and latency
  • admission-webhook failures
  • current versus desired replicas
  • time from event arrival to a ready worker
  • work backlog and oldest-event age
  • scaling limited by the configured maximum
  • unschedulable Pods after workload scale-up

Alerting only on KEDA Pod health misses the failure mode where KEDA is running but cannot reach the trigger source.

9. Upgrade and rollback

Before upgrading:

  1. read the KEDA release and migration notes
  2. compare CRDs and rendered manifests
  3. confirm the Kubernetes compatibility range
  4. test existing ScaledObjects and authentication resources
  5. record the current Helm release and values

bash

helm get values keda -n keda > keda-values-before-upgrade.yaml
helm history keda -n keda

Helm rollback can restore namespaced resources, but CRD schema changes may require separate handling. Never assume a chart rollback reverses CRDs automatically. Keep the prior workload-autoscaling configuration available until the new version passes scale-up, scale-down, trigger-failure, and authorization tests.

Production-readiness boundary

KEDA is ready for production only when the complete scaling system has been tested: trigger availability, credentials, external metrics, HPA behavior, application startup, node capacity, downstream limits, observability, and rollback. Installing three healthy KEDA Deployments is necessary, but it is not sufficient evidence.

References