DEV Community

Muskan Bandta
Muskan Bandta

Posted on

KEDA 3.0 Scale-to-Zero: How We Cut Intermittent Kubernetes Workload Costs to Almost Nothing

KEDA 3.0 just landed, and the headline feature is the one I care about most as someone who watches a cloud bill: event-driven autoscaling now covers 80+ event sources (Kafka, RabbitMQ, and a long list more) with proper scale-to-zero. If you run workloads that sit idle most of the day and spike when work arrives, this is the difference between paying for capacity you use and paying for capacity that waits.

I have been moving our intermittent workloads onto this pattern, so here is what scale-to-zero actually does to the bill, where it helps, and the sharp edges nobody mentions.

The problem: HPA scales to one, not to zero

Standard Horizontal Pod Autoscaler has a floor. minReplicas cannot be zero, so a workload that processes a queue twice a day still keeps at least one pod (and often the node under it) running 24/7. For a consumer that is busy 2 hours a day, you are paying for 22 hours of nothing.

KEDA changes the shape of the question. Instead of "how many replicas does current CPU justify," it asks "are there events waiting." No events, zero pods. Events arrive, it scales from zero up to whatever the load needs. That floor of zero is the whole game for intermittent work.

Where scale-to-zero actually pays off

Not every workload benefits. The ones that do share a profile: bursty, event-triggered, and tolerant of a short cold start. In our environment the clear wins were:

  • Queue consumers. A worker draining an SQS or RabbitMQ queue that fills a few times a day. Idle 80%+ of the time, now scales to zero between bursts.
  • Kafka stream processors for low-volume topics that only see traffic during business hours.
  • Scheduled batch jobs dressed up as long-running services because nobody wanted to re-architect them. Scale-to-zero gets most of the savings without the rewrite.
  • Dev and staging consumers that had no reason to run overnight and did anyway.

A rough sizing rule I use: if a workload is idle more than half the day and an extra few seconds of latency on the first event is acceptable, it is a scale-to-zero candidate.

A minimal example

Here is the shape of a ScaledObject that scales a consumer from zero based on queue depth:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-worker
spec:
  scaleTargetRef:
    name: order-worker
  minReplicaCount: 0
  maxReplicaCount: 20
  cooldownPeriod: 300
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.us-east-1.amazonaws.com/1234/orders
        queueLength: "20"
Enter fullscreen mode Exit fullscreen mode

minReplicaCount: 0 is the line that matters. queueLength: "20" means KEDA targets roughly one pod per 20 in-flight messages. cooldownPeriod is how long it waits after the last event before scaling back to zero.

The sharp edges (learned these the annoying way)

Scale-to-zero is not free of tradeoffs. Three things bit us:

1. Cold starts are real. From zero, the first event waits for a pod to schedule, pull its image, and start. For a lightweight consumer that is a few seconds. For a fat image or a JVM with a slow warmup, it can be much worse. Fixes: shrink the image, keep it warm on the node (image pre-pull), or set minReplicaCount: 1 for anything latency-sensitive and accept the smaller saving.

2. The node still has to exist. KEDA scales pods to zero, but if scaling to zero leaves a node empty, you only capture the saving when the cluster autoscaler (or Karpenter) actually removes that node. Scale-to-zero without node-level scale-down is half a win. Make sure your node autoscaler is configured to consolidate and remove empty nodes, or the pods vanish while the bill does not.

3. cooldownPeriod is a cost lever, not a default. Too short and you thrash, scaling up and down and paying repeated cold starts. Too long and you keep pods alive well past the last event. We tuned this per workload rather than trusting the default.

What it did to the bill

Across the intermittent workloads we moved, the pods themselves spent roughly 70-85% less wall-clock time running, and once we fixed the node-consolidation gap, most of that translated into actual node-hour savings rather than just idle pods disappearing. The exact number depends entirely on how idle the workload was to begin with, which is the honest answer: scale-to-zero pays in proportion to how much you were overpaying before.

The bigger point is architectural. Scale-to-zero reframes intermittent workloads from "always-on services that happen to be idle" to "functions that exist only when there is work." That is the right mental model for cost, and KEDA 3.0's expanded trigger list means far more of your workloads can adopt it without custom glue.

If you have rolled out KEDA scale-to-zero, what tripped you up first, cold starts or the node-scaledown gap? For us it was the node gap, we celebrated the pod count dropping before realizing the nodes were still there.

Top comments (0)