Kubernetes Metrics API goes GA in 1.37: how to migrate from beta without breaking HPA and VPA
metrics.k8s.io moved from v1beta1 to stable v1. The path to update metrics-server, validate the new API with kubectl, and check your autoscalers before upgrading to 1.37 in production.

Starting with Kubernetes 1.37, the Metrics API (metrics.k8s.io) graduated to GA, served at the stable v1 version. This is the API that feeds kubectl top, the HorizontalPodAutoscaler (HPA), and the VerticalPodAutoscaler (VPA) with CPU and memory usage from nodes and pods. The change was documented in the official docs on July 1, 2026 ("Add docs for metrics.k8s.io graduation to stable").
The point that matters for whoever operates the cluster: v1beta1 doesn't disappear overnight, but it now coexists with v1, and the safe path is to validate everything that consumes the API before running the control plane upgrade to 1.37. An autoscaler that can't find a metric doesn't scale, and during a traffic spike that's not a yellow alert, it's downtime.
What exactly changed
The pipeline architecture remains identical: cAdvisor → kubelet (endpoint /metrics/resource) → metrics-server → API server → consumers (HPA, VPA, kubectl top). What graduated was the API contract. Before, a query returned "apiVersion": "metrics.k8s.io/v1beta1". Now the stable version responds with v1:
{
"kind": "NodeMetrics",
"apiVersion": "metrics.k8s.io/v1",
"metadata": { "name": "minikube" },
"timestamp": "2022-01-27T18:48:33Z",
"window": "30s",
"usage": { "cpu": "487558164n", "memory": "732212Ki" }
}The schema of the NodeMetrics and PodMetrics objects (usage, window, timestamp fields) hasn't changed. This is what makes the migration low risk: anyone consuming via kubectl top or native HPA/VPA doesn't need to rewrite anything. The risk lies with whoever queries the API in a hardcoded way, pinning /apis/metrics.k8s.io/v1beta1/... in scripts, dashboards, or homegrown operators.
Worth remembering what the docs themselves reinforce: the Metrics API delivers only the minimum CPU and memory for autoscaling. If you need business or latency metrics to scale, that remains on the Custom Metrics API, which is a different pipeline (and isn't affected by this graduation).
Prerequisites before touching the cluster
- Compatible metrics-server: the reference implementation of the Metrics API is metrics-server. Make sure you have a version that serves
v1. Check the releases in the official repository (kubernetes-sigs/metrics-server) and the compatibility matrix with your Kubernetes version before deciding on the tag. - API aggregation layer enabled: the Metrics API is served via the aggregation layer, with an
APIServiceregistered formetrics.k8s.io. In a kubeadm cluster this already comes enabled; on managed offerings (EKS, GKE, AKS) it's the provider who handles it. - kubelet on the right endpoint: metrics-server v0.6.0+ reads from
/metrics/resource. Older versions use/stats/summary. If you're on a very old metrics-server, upgrading already solves two problems at once.
My recommended order: first metrics-server, validate, then the control plane to 1.37. Never both in the same step, because if something breaks you won't know which change was to blame.
Step 1: find out who's still speaking v1beta1
Before anything else, map out what the cluster exposes today:
kubectl api-versions | grep metrics.k8s.ioIf only metrics.k8s.io/v1beta1 shows up, your metrics-server still isn't serving v1. Next, hunt for hardcoded consumers in your own code and manifests:
grep -rn "metrics.k8s.io/v1beta1" ./infra ./charts ./scriptsDashboards that run kubectl get --raw "/apis/metrics.k8s.io/v1beta1/..." are the first candidates to break. HPAs and VPAs created via YAML don't reference the Metrics API version directly, so they're usually safe; even so, list them to revalidate later:
kubectl get hpa -A
kubectl get vpa -A 2>/dev/nullStep 2: update metrics-server
Editor's note: the code in this section was not tested in a real environment. Validate before using in production.
On a kubeadm or bare-metal cluster, apply the manifest for the chosen version:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/<VERSAO>/components.yamlThe classic stumbling block here, especially on kubeadm and clusters with self-signed kubelet certificates: metrics-server can't validate the kubelet's TLS and the pod ends up in CrashLoopBackOff or the APIService reports False. The symptom in the logs looks something like unable to fully scrape metrics: x509: certificate signed by unknown authority. The known fix is to add the flag to the container:
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,ExternalIP--kubelet-insecure-tls only in a controlled environment; the right approach in production is to configure the kubelet with a certificate signed by a CA that metrics-server trusts. It's an explicit trade-off: skipping TLS verification is a quick fix, but it opens a surface you'll want to close later.
On managed offerings, you generally don't manage metrics-server by hand; the cluster version upgrade already brings the compatible version. It's worth confirming with the provider whether the add-on you use (or the one installed via Helm) serves v1.
Step 3: validate that v1 responds
After the rollout, confirm the stable API is up:
kubectl api-versions | grep metrics.k8s.io
# expected: metrics.k8s.io/v1
kubectl get apiservice v1.metrics.k8s.io -o jsonpath='{.status.conditions[?(@.type=="Available")].status}{"\n"}'
# expected: TrueQuery a node directly through the new version:
kubectl get --raw "/apis/metrics.k8s.io/v1/nodes/$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')" | jq '.apiVersion'
# "metrics.k8s.io/v1"And the test that matters most in practice, kubectl top:
kubectl top nodes
kubectl top pods -AIf kubectl top returns numbers, the whole pipeline is functional: kubelet collecting, metrics-server aggregating, API server serving. If it returns error: Metrics API not available, something between metrics-server and the aggregation layer is broken, go back to Step 2.
Step 4: confirm HPA and VPA are actually working
The API responding isn't the same thing as the autoscaler working. Check whether the HPA is reading the metric:
kubectl describe hpa <nome> -n <namespace>What you want to see in Conditions is AbleToScale: True and ScalingActive: True. If ScalingActive: False shows up with reason FailedGetResourceMetric, the HPA isn't getting the metric, even if kubectl top works. It's usually a permissions issue or metrics-server hasn't populated the cache yet (give it a few minutes after the rollout).
For VPA, besides making sure the VPA's own pods (recommender, updater, admission-controller) are Running, check whether recommendations are still being generated:
kubectl describe vpa <nome> -n <namespace> | grep -A6 "Recommendation"A VPA that stopped recommending after the upgrade is a sign that the recommender lost the metrics pipeline.
Step 5: upgrade to 1.37
With metrics-server serving v1, kubectl top responding, and HPAs showing ScalingActive: True, then yes, the control plane upgrade to 1.37 is safe. On kubeadm, the standard flow (kubeadm upgrade plan → kubeadm upgrade apply → drain and update nodes). After the upgrade, repeat Step 3 and Step 4: it's quick, and it's the difference between finding out about a dead autoscaler now, during the maintenance window, or at 3am during a spike.
What remains open
The docs mark the graduation, but don't pin down a timeline for removing v1beta1. Historically, Kubernetes keeps the beta version around for a few releases after GA, so there's slack to migrate scripts without rushing. Even so, it's already worth treating hardcoded v1beta1 as technical debt to pay off: switch to v1 in your dashboards and operators while the beta still exists, so you're not caught off guard when it finally goes away. Full official reference at kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline.
Source 1: Kubernetes Docs: Resource Metrics Pipeline (Metrics API) (https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/)
Translated from the Brazilian Portuguese original · Read the original
Kubernetes: The Practical Guide to Migrating from PodSecurityPolicy to Pod Security Admission
The admission controller that replaced PodSecurityPolicy has been stable since Kubernetes 1.25, but configuring the privileged, baseline, and restricted profiles per namespace still breaks workloads that weren't audited before enforcement.
