Skip to main content

Kubernetes 1.37 Let HPA Scale to Zero: Do You Still Need KEDA?

9 min readDora NodaDora Noda
Share
On this page

Kubernetes 1.37 "Garhwal" shipped on August 26, 2026 with 67 enhancements, and one quiet graduation matters more to your idle-bill than all the AI-scheduling headlines: HPAScaleToZero (KEP-2021) moved from alpha to beta, enabled by default. Set minReplicas: 0 on an HPA and your workload can now idle to zero pods and wake back up with no feature gate and no add-on installed.

So here is the verdict up front: for HTTP workloads already sitting behind Prometheus or ingress-request metrics, you can now delete KEDA's HTTP add-on and pocket roughly two-thirds of your idle node footprint — about €32/month per 20 idle preview apps on current Hetzner pricing in the worked example below. But "no gate, no KEDA" is not "no work": CPU and memory metrics cannot wake a workload that has zero pods, so you still need an external signal that exists independently of your pods, plus something that holds requests during the cold start. And if you scale on anything that is not HTTP — queues, streams, cron-like events — KEDA is untouched by this graduation and stays.

Read on for the mechanics, the two hard requirements with a concrete YAML sketch, the full before/after math with a KEDA baseline row, and the decision table.

What 1.37 actually changed

Since Kubernetes 1.16, the HPA controller has contained a small conditional: allow minReplicas: 0 when the HPAScaleToZero feature gate is on. For a decade it sat in alpha, off by default — not because it was unstable, but largely through KEP-process inertia. The promotion commit landed June 11, 2026, graduating the gate to beta and flipping the default to true for v1.37. Homelab operators noticed within days of the release: clusters on 1.37 could drop their explicit HPAScaleToZero=true controller-manager flags, confirmed against upstream's own versioned feature list.

Three mechanics matter:

  • spec.minReplicas: 0 is now accepted by default. The API validation for MinReplicas moved to the declarative validation framework and dynamically respects the gate — on 1.37+ servers it just passes.
  • It works only with object and external metrics. This is the load-bearing constraint. A Deployment with zero pods produces no CPU or memory utilization numbers, so there is nothing for the HPA controller to observe and no value that can cross a threshold to wake the workload back up. The signal must live outside the pods: requests-per-second from the ingress, queue depth from a broker, a Prometheus query — anything queryable while replica count is zero.
  • A new HPA status condition tracks the zero state, so kubectl describe hpa tells you the scaler deliberately parked the workload rather than failing to compute replicas.

One caution for mixed fleets: the gate is enforced server-side by the kube-controller-manager. During a rolling control-plane upgrade where an older controller-manager is still leader, minReplicas: 0 behavior follows whichever controller is actually running — verify the gate state on the serving version (more on this in the guardrails).

The two things "just works" still needs

Dropping to zero is the easy direction — the metric falls to nothing and replicas follow. Waking up is where teams get bitten. You need exactly two things, and both must exist before you set minReplicas: 0:

1. An external signal that survives zero. CPU/memory are out, as above. In practice this means a Prometheus Adapter query (e.g. per-service HTTP requests per second), an ingress controller's request metric exposed as an external metric, or a queue-length metric. A minimal sketch:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: preview-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: preview-app
  minReplicas: 0
  maxReplicas: 5
  metrics:
    - type: External
      external:
        metric:
          name: http_requests_per_second
          selector:
            matchLabels:
              service: preview-app
        target:
          type: AverageValue
          averageValue: 10
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300

Note the stabilizationWindowSeconds: without a generous scale-down window, a workload flaps between zero and one on every stray health check or scraper hit. Five minutes of quiet before parking is a sane default for preview environments.

2. Something that buffers requests during the cold start. When the first real request arrives at zero replicas, some component must hold the connection open while pods start — otherwise the request 502s and your "scale to zero" reads as an outage. Knative solved this years ago with its activator, which intercepts traffic, buffers it, and forwards once pods are ready; KEDA's HTTP add-on runs the same pattern with its interceptor proxy. Native HPA gives you the scaling decision but no buffering component — you must supply one (an ingress with retry-on-503 and a queue, a Knative-style activator, or the KEDA HTTP add-on's interceptor kept purely as a buffer). Measure your own cold start — community comparisons put Knative-style wakeups around 3 seconds for typical images — and confirm it fits the workload's SLO before parking anything latency-sensitive.

The worked math: what zero actually saves on owned metal

Assumptions, stated plainly so you can substitute your own: a fleet of 20 preview/staging apps, each with requests of 500m CPU / 1Gi RAM, idle roughly overnight and between reviews. Node SKU: Hetzner CX43 (8 shared vCPU, 16 GB RAM, €15.99/month, post-June-2026 pricing). Bin-packing at ~80% allocatable gives roughly 6.4 vCPU / 12.8 Gi usable per node.

SetupReserved footprintNodes (bin-packed)€/month
Before: every app min 1 replica20 × 500m / 1Gi = 10 vCPU / 20 Gi2 nodes€31.98
Native HPA to zero (1.37)Activator + Prometheus Adapter ≈ 1 vCPU / 1 Gi always on1 node (shared with other always-on infra)~€10–16
KEDA HTTP add-on baselineKEDA operator + interceptor ≈ 1 vCPU / 1 Gi always on1 node~€10–16

Net savings for this fleet shape: about one full node, ~€16/month — roughly half the idle footprint, once you subtract the always-on overhead both approaches carry. The honest headline: native HPA and KEDA cost nearly the same to run; the win is deleting KEDA's CRDs, upgrade cycle, and second autoscaling control loop from your operations, not deleting its CPU requests.

Sensitivity — because one point flatters:

  • 25% of apps latency-critical (kept at min 1): 5 apps always resident ≈ 2.5 vCPU / 5 Gi, plus overhead — still fits one node with room. Savings hold.
  • 50% latency-critical: ~5 vCPU / 10 Gi resident — a second node stays. Savings roughly halve.
  • Above ~50 idle apps: the freed capacity compounds to two or more nodes, and the savings start paying for real hardware instead of rounding error.
  • Wake SLO under ~1s: neither native HPA nor KEDA HTTP is suitable; keep min 1 and stop reading — the cold start dominates everything else.

The pattern this exposes: scale-to-zero pays on flat-rate metal exactly in proportion to your idle share. Metered clouds save per-second; owned boxes save per-node-freed. Small fleets should expect tens of euros per month and a simpler stack — worth doing, not worth a re-architecture.

So do you still need KEDA?

WorkloadNative HPA (1.37+)KEDA ScaledObjectKEDA HTTP add-onKnative KPA
HTTP idling to zero, Prometheus/ingress metrics presentBest fit — no add-onWorks, extra moving partsWorks, but now redundant with native + a bufferWorks, heaviest option
HTTP with no external metric pipelineBlocked until you build oneBest fit (its own metrics path)Viable via interceptor metricsViable
Queue / stream / event-driven (Kafka, RabbitMQ, SQS, cron)Not supportedBest fit — 50+ scalers, IsActive activation handles 0↔1N/AN/A
Need request buffering on wakeBring your ownBring your ownBuilt in (interceptor)Built in (activator)
Ops costZero new componentsOperator + CRDs + per-trigger configOperator + interceptor + CRDsFull Serving stack

The rule of thumb: if your wake signal is HTTP and you already run the metrics pipeline, graduate to native HPA and retire the HTTP add-on. If your wake signal is anything else, KEDA is still the answer — this beta changes nothing about event-driven scaling, and KEDA's activation phase (its explicit 0↔1 logic per scaler) remains the most mature implementation of exactly that transition.

Four guardrails before you set minReplicas: 0

  • Verify the serving version first: kubectl -n kube-system get pods -l component=kube-controller-manager -o yaml | grep -i hpa-scale should show nothing forced off, and every control-plane node must run 1.37+. During mixed-version upgrades the oldest controller-manager behavior wins.
  • Never roll minReplicas: 0 fleet-wide in one change. Start with preview/staging namespaces, watch wake latency for a week, then expand. The blast radius of a missing metric pipeline is every workload silently parked with no way to wake.
  • Pair every zero-scaled HPA with its wake metric and buffer explicitly. An HPA with minReplicas: 0 on CPU metrics is a workload that parks on first idle and never wakes — lint for type: Resource combined with minReplicas: 0 in CI.
  • Alert on zero-duration, not just replica count. A workload parked for its normal 12 idle hours is savings; one parked for 72 hours straight may be a broken wake signal. Alert when time-at-zero exceeds the workload's known idle pattern.

The boring release that pays

Kubernetes 1.37 is being called a deliberately boring release — incremental graduations, no concepts to re-architect around — and HPA scale-to-zero is the best example of why boring is good. A decade-old alpha conditional, flipped on by default, quietly deletes an add-on from thousands of clusters and halves the idle footprint of every preview environment it touches. No migration project, no new CRDs: upgrade the control plane, add an external metric, keep a buffer in front, and collect the node back.

Running preview environments on machines you own? Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on your own hardware, with per-app autoscaling that respects min/max replicas you declare. Star the repo on GitHub.

Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex