Kubernetes 1.37: What Native HPA Scale-to-Zero Changes for Platform Teams

Keeping one worker alive just in case rarely looks expensive in isolation. Repeat that decision across applications, environments, and clusters, and the idle capacity starts to matter. The effect is especially visible when workers reserve GPUs or dedicated CPUs.
Kubernetes 1.37 makes an important change to that operating model: HorizontalPodAutoscaler scale-to-zero is now beta and enabled by default. With an appropriate object or external metric, an HPA can remove the final idle replica and restore capacity when demand returns. The Kubernetes 1.37 release announcement confirms the graduation.
For platform teams, the interesting part goes beyond allowing a zero in a configuration file. It changes how we design the demand signal, how we think about availability, and what we must measure before claiming a cost saving.
Why Zero Needs a Different Signal
CPU and memory utilization describe running Pods. Once every Pod has disappeared, those measurements cannot tell the controller that new work is waiting.
A queue is different. Its backlog exists independently of the workers consuming it. An external metric can remain observable while the worker Deployment has zero replicas. The controller can therefore see demand before the application is running.
The sequence becomes straightforward: the workers are idle, the HPA scales them down, new work enters the queue, and the external signal causes capacity to return.
Kubernetes requires at least one object or external metric when minReplicas is zero; a resource-metrics-only configuration is rejected. CPU or memory alone cannot provide the activation signal. The upstream feature gate is HPAScaleToZero, and it must be enabled on both the API server and controller manager. See the HPA documentation
.
For an existing platform, I would first check whether the desired metric is actually available to the HPA. A graph in a monitoring dashboard does not establish that the Kubernetes metrics API exposes a suitable signal. The adapter, query, permissions, and metric identity still need to line up.
Native activation can simplify an architecture, but it does not make the demand signal appear automatically.
An Empty Queue and a Missing Metric Are Different States
The most important reliability question is what happens to the metric when the worker is absent.
If the only exporter runs inside the workload being scaled, removing the final Pod may remove the activation signal too. That creates a circular dependency: the worker must run to report the need for a worker.
I would design the metric collection path to remain available independently of the worker Deployment. I would also test the difference between a valid zero, a stale value, and a failed query.
An empty queue is an observation about demand. A failed metric request is an observation about the monitoring path. Converting both into zero conceals the exact failure that can leave work stranded.
For that reason, my acceptance criteria would include metric freshness, adapter availability, and an alert for a growing backlog with no ready workers. The event source, collection path, and controller together form the activation mechanism. Each needs an owner and a failure policy.
Queue depth itself also needs a precise definition. Does it include only messages waiting for delivery, or also messages already assigned to workers? A queue can report no waiting messages while a worker is still processing a long task. A scale-down decision based on that number needs to be compatible with the application’s shutdown and redelivery behaviour.
Zero Replicas Can Mean Idle—or Paused
There is a second ambiguity: who put the workload at zero?
An HPA that deliberately removed its last replica should keep watching for demand. A workload that an operator has deactivated should not necessarily restart just because a metric rises.
The HPA records ScaledToZero=True in its status when it owns the transition to zero. That condition allows subsequent reconciliations to activate the workload again. After reactivation, the condition becomes false with the reason NotScaledToZero. A zero-replica workload without the true condition remains paused.
One historical detail is worth getting right: the condition and associated controller behaviour arrived in the 1.36 alpha implementation. Version 1.37 graduates the feature to beta and enables it by default. The dedicated Kubernetes feature announcement explains this progression.
This distinction matters during initial deployment. Creating a fresh Deployment directly at zero, alongside an HPA whose minimum is zero, is not equivalent to letting the HPA scale a running workload down. The design explicitly calls out this bootstrap trap in KEP-2021 .
I would validate initial activation as a separate test, then observe an HPA-controlled transition from running to zero and back. During troubleshooting, inspect the HPA conditions and events alongside the Deployment replica count. The number alone does not explain why the workload is stopped.
Cold Start Becomes Part of the Service Objective
With one warm worker, a message can begin processing shortly after delivery. With no workers, the system first has to notice the work and create execution capacity.
The latency budget now includes metric collection, controller evaluation, scheduling, image availability, application initialization, and readiness. A GPU workload may also load a model or initialize an accelerator. If the required node capacity has been removed, provisioning a node adds another stage.
For a durable queue, this can be a reasonable trade-off. It depends on how long work is allowed to wait, not merely whether the application is described as asynchronous.
I would measure time from message arrival to the first useful processing action, then time to completion. Measuring only Pod startup can miss a substantial part of the delay. Repeat the test with cached and uncached images, available and unavailable node capacity, and a realistic application initialization path.
The scale-down policy deserves similar attention. The standard HPA downscale stabilization window is five minutes, and it is configurable through spec.behavior.scaleDown. That prevents every brief lull from immediately removing capacity. The scale-to-zero announcement
notes that normal stabilization behaviour still applies.
I would choose the idle window using observed arrival patterns and restart cost. Frequent cold starts can make an aggressively minimized replica count a poor operating choice.
HTTP Requests Need Somewhere to Wait
A queue gives work a place to remain while capacity is absent. A normal Kubernetes Service does not provide that waiting room for HTTP requests when no backend Pod is ready. Request-driven activation therefore needs another mechanism, such as a buffering or activation layer. This boundary is also explicit in the Kubernetes feature explanation .
Before applying the pattern to an API, I would ask what happens to the first request after an idle period. Which component accepts it? How long can it wait? What response does the caller receive if activation fails? Is retrying safe?
If the latency requirement cannot tolerate activation, retaining a warm replica may be the correct design. Scale-to-zero is an option for matching capacity to a service contract; it does not replace that contract.
Released Resources Are Not Automatically Financial Savings
Removing an idle Pod frees the resources it reserved. That can make room for other workloads. It does not necessarily remove a billable virtual machine or reduce a committed infrastructure cost.
Node autoscaling is a separate mechanism. It can consolidate or remove eligible nodes, subject to its configuration and the workloads that remain. Kubernetes documents that distinction in its node autoscaling overview .
For a GPU worker, I would examine the full chain: the worker disappears, its node becomes eligible for removal, the node autoscaler acts, and the infrastructure billing model reflects the change. Shared workloads, minimum node counts, scheduling constraints, or existing commitments can change the outcome.
On fixed hardware, the benefit may instead be reclaimed capacity for another team. That is valuable, but it should be described as utilization improvement rather than an assumed reduction in spending.
The useful FinOps comparison is between complete operating policies: a warm worker, a cold worker on a warm node, and a cold worker whose node also needs provisioning. Compare idle cost, recovery latency, and the operational effort of each.
Where KEDA Still Fits
KEDA offers an event-driven autoscaling ecosystem with integrations for many event sources, authentication mechanisms, and scaling models, including Jobs. Its architecture works with Kubernetes autoscaling rather than replacing every part of it. See the KEDA concepts documentation .
Native HPA scale-to-zero can remove the need for a separate activation component when a suitable metric already exists. That is a narrower decision than replacing KEDA across a platform.
I would compare the entire path from event source to scaling action. If removing KEDA means building and maintaining a bespoke adapter, credential lifecycle, and collection service, the result may not be simpler. If those capabilities are already operated centrally, native HPA may be a good fit.
Also distinguish a pool of long-running workers processing batch tasks from Kubernetes Job objects. This HPA enhancement adjusts a scalable workload; it does not turn the HPA into a Job scheduler.
What I Would Validate Before Enabling It Broadly
I would start with a low-risk queue consumer whose work is durable, replayable, and tolerant of startup delay. The following checks would form the initial evaluation plan, rather than a claim that the feature has been benchmarked here.
| Scenario | Evidence to collect |
|---|---|
| Sustained idle period | The HPA reaches zero after the intended policy window and records ownership of that state. |
| Work arrives at zero | The metric remains available, replicas return, and work completes within the latency objective. |
| Metric collection fails | The failure is distinguishable from zero demand and raises an actionable signal. |
| A worker is interrupted during processing | Work is completed or safely redelivered without an unintended duplicate business effect. |
| The workload is deliberately paused | The observed conditions and reactivation procedure match the maintenance runbook. |
| No suitable node is available | Provisioning delay and capacity failures are visible in the end-to-end result. |
| Repeated small bursts | The policy avoids excessive restart cycles while meeting the waiting-time objective. |
I would keep a documented fallback to warm capacity and test the transition while the workload is at zero. Configuration changes, maintenance procedures, and GitOps reconciliation should not leave ownership of the replica count ambiguous.
A Smaller Minimum, a More Explicit Architecture
The appeal of this change is practical: the last idle worker no longer has to remain solely to preserve a path back to activity. For queue consumers and other suitable asynchronous workloads, that is a useful native capability.
The operating model still needs a reliable external signal, a safe shutdown path, an acceptable activation delay, and a clear explanation of where the savings come from.
Those are the questions I would bring to an architecture review. Getting to zero is the visible milestone. Returning from zero reliably is what makes the design useful.