, ,

KEDA: How Event-Driven Autoscaling Cuts Kubernetes Cost

Kubernetes clusters run at an average of 8% CPU utilization, according to Cast AI’s 2026 State of Kubernetes Optimization Report across 23,000+ production clusters. A key driver is workloads that sit idle between jobs, holding node reservations even when there is no work to process. KEDA, the CNCF-graduated Kubernetes Event-Driven Autoscaler, closes that gap by…

ARIA Avatar

Key takeaways

  • KEDA is a CNCF Graduated open-source project (graduated August 22, 2023), created by Microsoft and Red Hat.
  • It scales Kubernetes workloads using external event signals: queue depth, Kafka consumer lag, Prometheus metrics, and 70+ additional sources.
  • KEDA wraps Kubernetes HPA rather than replacing it. When you create a ScaledObject, KEDA creates and manages an HPA resource automatically.
  • Scale to zero is KEDA’s most impactful cost feature. HPA cannot go below 1 replica; KEDA patches replicas directly to 0 when no events are pending.
  • Cold starts are the trade-off of scale-to-zero. Mitigate with lean container images, tuned readiness probes, and per-workload minReplicaCount decisions.
  • Cast AI handles the node layer: provisioning instances, placing pods on spot, and right-sizing resource requests so KEDA-scaled pods land on minimum-cost nodes.

What is KEDA?

Most queue-based workloads sit idle between processing bursts, holding node reservations and consuming compute budget without doing useful work. KEDA (Kubernetes Event-Driven Autoscaling) addresses this directly. It scales workloads based on external event signals such as queue depth or Kafka lag, rather than CPU or memory alone. When no events are pending, KEDA takes the Deployment all the way to zero replicas, eliminating idle cost. Microsoft and Red Hat created the project; it reached CNCF Graduation on August 22, 2023, with over 10,000 GitHub stars and 400+ contributors confirming production maturity.

The most important capability separating KEDA from standard HPA: KEDA can scale to zero. HPA enforces a minimum of 1 replica with no mechanism to reach zero. KEDA bypasses that constraint at the controller level, patching the replica count directly when all event sources report empty.

Key CRDs

KEDA introduces four primary CRDs:

  • ScaledObject: Links a Deployment or StatefulSet to one or more event source triggers. It defines min/max replicas, polling interval, and cooldown period.
  • ScaledJob: Targets Kubernetes Jobs instead of long-running Deployments. Use ScaledJob when each event should spawn an isolated Job instance rather than routing to a shared worker pool. This is the right choice for one-shot batch tasks where job isolation matters more than pooled efficiency, for example image processing or report generation where shared state is a liability.
  • TriggerAuthentication: Manages credentials for scalers that need to authenticate to external systems, for example storing an AMQP connection string for RabbitMQ.
  • ClusterTriggerAuthentication: A cluster-scoped variant of TriggerAuthentication, reusable across multiple namespaces.

KEDA supports Deployments, ReplicaSets, StatefulSets, and Jobs. It runs across AWS EKS, GCP GKE, Azure AKS, and on-premises clusters without cloud-vendor lock-in.

How event-driven scaling works

KEDA deploys three pods into your cluster: the KEDA Operator (keda-operator), the Metrics API Server (keda-metrics-apiserver), and the Admission Webhooks server (keda-admission-webhooks).

The KEDA Operator

The KEDA Operator watches for ScaledObject and ScaledJob CRDs. When you create a ScaledObject, the operator creates and manages an HPA resource targeting your specified Deployment. It polls external event sources continuously to determine whether scaling should activate or deactivate.

The Metrics API Server

The keda-metrics-apiserver implements the external.metrics.k8s.io API. This bridges Kubernetes and external event sources so the HPA controller can query queue depths, Kafka consumer lag, or Prometheus values as if they were native cluster metrics. Therefore, HPA’s existing scaling logic applies without modification.

The Admission Webhooks

The keda-admission-webhooks pod validates ScaledObject and ScaledJob CRDs at creation time. Before Kubernetes commits a ScaledObject to etcd, the webhook checks that the target resource exists and the trigger configuration is valid. It also verifies the referenced TriggerAuthentication is present. This means misconfigured ScaledObjects fail fast at apply time rather than silently misbehaving at runtime. All three pods must be healthy for KEDA to function correctly.

Scaling flow

  1. You create a ScaledObject referencing a Deployment and a trigger, for example a RabbitMQ queue.
  2. The Admission Webhook validates the ScaledObject before Kubernetes commits it.
  3. The KEDA Operator detects the validated ScaledObject and creates an HPA targeting the Deployment.
  4. The Metrics API Server queries the external event source for current queue depth.
  5. HPA reads the metric via external.metrics.k8s.io and scales the Deployment accordingly.
  6. When the queue empties, KEDA directly patches the Deployment replica count to 0, bypassing HPA’s minimum-1 constraint.

KEDA scalers

A scaler is KEDA’s abstraction for connecting to an external event source. Each entry in a ScaledObject’s triggers list corresponds to one scaler. KEDA ships with 70+ built-in scalers. Common examples include:

  • rabbitmq: Scales on queue length or queue message rate.
  • kafka: Scales on consumer group lag per topic.
  • aws-sqs-queue: Scales on ApproximateNumberOfMessagesVisible.
  • prometheus: Scales on any PromQL query result.
  • azure-servicebus: Scales on Azure Service Bus topic or queue depth.

KEDA also supports custom scalers through the External Scaler gRPC interface, so you can connect KEDA to any internal or proprietary metric source.

Scale to zero and cold starts

Scale-to-zero is what makes KEDA materially different from HPA in cost terms. Here is exactly how it works.

How scale-to-zero works

When all triggers in a ScaledObject report zero events (empty queue, zero lag, no metrics above threshold), KEDA patches the Deployment replica count directly to 0. This deliberately bypasses HPA. Standard HPA enforces a minimum of 1 replica and cannot eliminate idle pods. KEDA bypasses that constraint at the controller level.

When a new event arrives, KEDA detects it on the next polling interval. It then sets replicas to 1, and HPA takes over for further scale-out based on the metric value. Note that when a Deployment is at 0 replicas, only KEDA polls at pollingInterval. The managed HPA does not activate until the first replica exists. This means the maximum wait time on the first event after scale-to-zero is one pollingInterval (default: 30 seconds, or 10 seconds in the example YAML below).

Cold-start implications

Scaling from 0 to 1 requires Kubernetes to schedule a pod, pull the container image (if not cached on the node), and complete initialization before processing begins. For queue-based workloads, the first messages in a burst typically experience a delay of a few seconds to over a minute, depending on image size and startup behavior.

This trade-off is acceptable for batch workers and background consumers. However, it is not acceptable for latency-sensitive request processors. Know your workload before enabling scale-to-zero.

Mitigations

  • Set minReplicaCount: 1 for latency-sensitive workloads. Reserve scale-to-zero for batch jobs and background consumers.
  • Optimize container images: Use multi-stage builds and minimize layer count. Pre-pull images using a DaemonSet or node image caching policy.
  • Tune cooldownPeriod: A longer cooldown (60-300 seconds) prevents rapid oscillation during intermittent bursts.
  • Use the KEDA-HTTP add-on for HTTP workloads: it proxies and buffers incoming requests during the cold-start window, preventing dropped connections.

KEDA vs HPA

KEDA does not replace HPA. It wraps and extends HPA. When you deploy a ScaledObject, KEDA creates and manages an HPA resource in the background. You get HPA scaling mechanics plus KEDA event triggers and scale-to-zero capability.

For a deeper look at HPA mechanics, see What Is Kubernetes HPA and How Can It Help You Save on the Cloud?

Comparison table

FeatureHPAKEDA
Scale to zeroNo (minimum 1 replica)Yes
Scaling signalsCPU, memory, custom metrics70+ external sources (queues, Kafka, Prometheus, etc.)
Event-driven triggersNoYes
Requires installationNo (built-in to Kubernetes)Yes (Helm chart or operator YAML)
Manages HPA under the hoodN/AYes
Works with Kubernetes JobsNoYes (ScaledJob)
Best forHTTP services, CPU-correlated workloadsQueue consumers, batch jobs, bursty event workloads

When to use HPA

Use HPA when your workload is an HTTP service that scales predictably with traffic. If CPU or memory correlates well with load and you never need scale-to-zero, HPA’s zero-installation setup wins on simplicity. Stateless API services are the canonical example.

When to use KEDA

Use KEDA when your workload consumes from a queue, Kafka topic, or SQS stream. Choose KEDA when your workload sits idle for hours or has unpredictable burst patterns: queue consumers, batch jobs, and webhook processors all fit this profile. For these patterns, scale-to-zero alone often justifies the additional setup cost. In contrast, forcing a queue-based consumer through HPA’s CPU-based scaling produces poor results. CPU does not rise until workers are already processing messages, introducing unnecessary lag in scaling response.

ScaledObject YAML

The following example configures KEDA to scale a RabbitMQ consumer Deployment based on queue length. It includes all required resources: the Kubernetes Secret for credentials, the TriggerAuthentication CRD, and the ScaledObject itself.

Installing KEDA

Before applying ScaledObject resources, install KEDA into your cluster with Helm:

helm repo add kedacore https://kedacore.github.io/charts
# Pin chart version to avoid breaking changes.
# Check https://github.com/kedacore/charts/releases for the latest stable release.
helm install keda kedacore/keda --namespace keda --create-namespace --version 2.14.0

This deploys all three KEDA pods into a dedicated keda namespace. Run kubectl get pods -n keda to confirm the operator, metrics server, and admission webhook are all running before proceeding.

Step 1: Create the Secret

apiVersion: v1
kind: Secret
metadata:
  name: keda-rabbitmq-secret
  namespace: default
type: Opaque
data:
  # Base64-encoded AMQP URI: amqp://user:password@rabbitmq:5672/vhost
  host: <base64-encoded-amqp-uri>

Step 2: Create the TriggerAuthentication

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: keda-trigger-auth-rabbitmq-conn
  namespace: default
spec:
  secretTargetRef:
    - parameter: host        # Maps 'host' field to the rabbitmq scaler
      name: keda-rabbitmq-secret
      key: host

The Kubernetes Secret pattern works for most environments. In production environments that use AWS IRSA, GCP Workload Identity, or Azure Managed Identity, KEDA’s TriggerAuthentication also supports pod identity authentication, eliminating the need to store credentials in a Secret.

Warning: If you have an existing HPA on this Deployment, remove it before applying the ScaledObject. Two controllers targeting the same Deployment will produce conflicting replica counts.

Step 3: Create the ScaledObject

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: rabbitmq-scaledobject
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1        # Explicit apiVersion avoids ambiguity with custom resources
    kind: Deployment
    name: rabbitmq-consumer
  minReplicaCount: 0              # Scale to zero when queue is empty
  maxReplicaCount: 20             # Maximum workers during peak load
  pollingInterval: 10             # Check queue depth every 10 seconds
  cooldownPeriod: 60              # Wait 60s after queue empties before scaling to 0
  fallback:
    failureThreshold: 3           # After 3 consecutive scaler failures, activate fallback
    replicas: 1                   # Hold 1 replica when the scaler cannot reach RabbitMQ
  triggers:
    - type: rabbitmq
      metadata:
        protocol: amqp
        queueName: orders         # Target queue name
        mode: QueueLength         # Scale on total messages in queue
        value: "5"                # 1 replica per 5 messages in queue
      authenticationRef:
        name: keda-trigger-auth-rabbitmq-conn

With this configuration, KEDA checks the orders queue every 10 seconds. Each group of 5 messages drives 1 additional replica, capped at 20 by maxReplicaCount. An empty queue triggers scale-to-zero after the 60-second cooldown. The fallback block is critical for production: if RabbitMQ becomes unreachable for 3 consecutive poll intervals, KEDA holds 1 replica instead of defaulting to the last-known count, preventing silent failure when your event source goes down.

Step 4: Verify the setup

After applying the ScaledObject, confirm KEDA is polling and the managed HPA was created:

# Check ScaledObject status, active trigger metrics, and any errors
kubectl describe scaledobject rabbitmq-scaledobject

# Confirm KEDA created a managed HPA targeting your Deployment
kubectl get hpa -n default

The describe output shows the current replica count, the last scaler poll result, and any errors reaching the event source. The HPA listing confirms KEDA created the managed HPA for your Deployment. If the HPA is absent, the ScaledObject likely failed admission; check kubectl describe scaledobject events for the specific reason.

How Cast AI complements KEDA

KEDA solves the pod-count problem. However, correct replica counts alone do not eliminate Kubernetes resource waste. Cast AI’s 2026 State of Kubernetes Optimization Report found average CPU utilization at just 8% across 23,000+ production clusters, with CPU overprovisioning at 69%. Those numbers reflect inflated resource requests that persist even when KEDA scales pod counts correctly.

The layered scaling model

Cast AI and KEDA address different layers of the same problem:

  • Layer 1 (pods): KEDA scales replica count from 0 to N based on queue depth, Kafka lag, or other event signals.
  • Layer 2 (nodes): Cast AI provisions and removes nodes in response to scheduling pressure from KEDA-driven pod changes, selects cheapest instance types, and places consumer workers on spot instances automatically.
  • Layer 3 (right-sizing): Cast AI Workload Autoscaler continuously right-sizes CPU and memory requests, ensuring KEDA-scaled pods land on nodes with minimal waste.

The right-sizing layer matters more than it appears. If your consumer pods request 2 CPU but routinely use 0.3, KEDA scaling to 10 replicas drives the cluster autoscaler to provision far more node capacity than the workload actually needs. Correcting those requests closes the loop between pod-level and node-level efficiency. For more on how automated right-sizing works in practice, see Automated Workload Rightsizing with PrecisionPack.

Spot instance synergy

KEDA-driven scale-to-zero workloads are strong candidates for spot instance placement. Queue consumers that tolerate termination and restart map cleanly to spot instance lifecycle. Cast AI’s spot management places KEDA-scaled workers on spot automatically, with fallback to on-demand when spot capacity is unavailable. Two cost levers compound: scale-to-zero eliminates idle costs, while spot pricing cuts active compute costs by 50-80%. Savings vary by workload type, cloud provider, region, and instance family; consistently interruptible workloads on major clouds typically achieve this range versus on-demand.

KEDA is also available as a built-in add-on for clusters managed by Cast AI. For teams running 10 or more clusters, this is a meaningful operational difference. Cast AI owns the KEDA Helm lifecycle, version upgrades, and cluster-by-cluster configuration across your fleet. That means KEDA does not become a separate dependency tracked through your own deployment pipelines, and version drift across clusters is not your problem to manage.

Conclusion

KEDA addresses a structural gap in Kubernetes autoscaling: workloads that need external event signals to scale correctly and must reach zero replicas when idle. For queue consumers, Kafka processors, and batch workers, KEDA is the practical choice over HPA alone. The setup is a Helm install and a few dozen lines of YAML.

The broader Kubernetes cost problem requires multiple layers working together. KEDA drives replica count from 0 to N based on queue depth or event lag. Cast AI responds by provisioning nodes and placing workers on spot instances, typically 50-80% below on-demand rates. Beyond cost, offloading node lifecycle management to Cast AI frees platform teams to focus on application delivery rather than infrastructure maintenance. It also right-sizes CPU and memory requests, which directly reduces how many nodes KEDA-scaled pods require. The result is a scaling loop that closes from event signal to instance type, without manual tuning at any layer.

For a full overview of Kubernetes autoscaling, including HPA, VPA, and cluster autoscaling mechanics, see Guide to Kubernetes Autoscaling for Cloud Cost Optimization.

What is KEDA?

KEDA (Kubernetes Event-Driven Autoscaling) is an open-source, CNCF-graduated component that scales Kubernetes workloads based on external event signals such as queue depth, Kafka consumer lag, Prometheus metrics, and 70+ other sources. Microsoft and Red Hat created it. KEDA graduated from the CNCF on August 22, 2023. It extends Kubernetes HPA by adding event-driven triggers and the ability to scale workloads to zero replicas when no events are pending.

KEDA vs HPA: what is the difference?

HPA scales workloads based on CPU and memory metrics built into the Kubernetes cluster. KEDA extends HPA to scale on external event sources such as queue depth and Kafka lag, and it adds scale-to-zero capability that HPA cannot provide. In practice, KEDA wraps HPA: when you create a ScaledObject, KEDA creates and manages an HPA resource under the hood. Use HPA for CPU-correlated HTTP services. Use KEDA for queue consumers, batch workers, and any workload that needs to scale to zero when idle.

Can KEDA scale to zero?

Yes. When all triggers in a ScaledObject report zero events, KEDA patches the Deployment replica count directly to 0. This bypasses HPA’s minimum-1 replica constraint. When a new event arrives, KEDA detects it on the next polling interval and restores replicas to 1, after which HPA manages further scale-out. Note that scaling from zero introduces a cold-start delay while the new pod schedules and initializes.

What event sources does KEDA support?

KEDA includes 70+ built-in scalers. These cover messaging systems (RabbitMQ, Kafka, AWS SQS, Azure Service Bus, Google Pub/Sub), databases, CI/CD systems, observability tools (Prometheus, Datadog), HTTP traffic, and more. Custom event sources are also supported through the External Scaler gRPC interface, allowing integration with any internal or proprietary metric system.

Does KEDA replace HPA?

No. KEDA wraps and extends HPA rather than replacing it. When you create a ScaledObject, KEDA creates a corresponding HPA resource and manages it automatically. You get all of HPA’s existing scaling mechanics plus KEDA’s external event triggers and scale-to-zero capability. For workloads already using HPA with CPU metrics, adding KEDA is additive, not disruptive.

Cast AIBlogKEDA: How Event-Driven Autoscaling Cuts Kubernetes Cost