Skip to main content

Kubernetes Metrics API Is Stable in v1.37: The Contract Agents Need Before Autoscaling Apps

12 min readDora NodaDora Noda
Share
On this page

An AI agent can read kubectl top in a few seconds. That does not mean it should be allowed to scale your production service.

Kubernetes 1.37 changes the foundation of that decision: the metrics.k8s.io API graduates from v1beta1 to stable v1 after nearly nine years in beta. The API contract is now durable, but it is deliberately small. It gives an agent CPU and memory observations; it does not tell the agent whether those observations are fresh, complete, tenant-authorized, or safe to turn into a replica change.

Here is the contract a self-hosted PaaS should expose before an agent gets a write path to autoscaling:

Evidence an agent must receiveMinimum contractSafe default action
Workload identityNamespace, workload, selector, and the exact Pod set being summarizedRefuse if the set crosses a tenant boundary or changes during evaluation
Resource observationsPodMetrics for every eligible Pod, with per-container CPU and memory usageRefuse a scale-down when any expected Pod is missing
Freshnesstimestamp, non-zero window, collection time, and an explicit maximum age; for example, reject samples older than two collection intervals or 60 secondsObserve only until fresh data returns
Resource meaningCPU as a rate over the reported window; memory as working set at collection time; requests used to calculate utilizationNever treat memory working set as a leak diagnosis or CPU as a request metric
Control boundsCurrent replicas, min/max replicas, quota headroom, rollout state, cooldown, and the proposed deltaClamp the proposal, then require policy approval
Decision trailInput samples, calculation, policy result, actor identity, and resulting scale statusApply only an auditable proposal

That table is the core answer for platform teams. Stable v1 makes the first row of the data contract easier to depend on. The remaining rows are the platform’s responsibility.


What Kubernetes v1.37 actually stabilizes

The Kubernetes project’s v1.37 announcement is refreshingly precise: the API version changes, but the resource types and fields do not. NodeMetrics reports CPU and memory for a node. PodMetrics reports CPU and memory for a Pod, including a per-container breakdown. There are no new semantics hidden behind the stable version.

The API was introduced as alpha in v1.6, became beta in v1.8, and has been used by kubectl top and the HorizontalPodAutoscaler for years. Stability means clients can build against a supported API surface instead of treating a beta endpoint as an indefinite compatibility promise.

There is still a transition detail that matters to an agent platform. Kubernetes 1.37 keeps v1beta1 available, and the project recommends that Metrics API implementations serve both versions for compatibility. kubectl top prefers v1 and falls back to v1beta1; the HPA controller in 1.37 still supports only v1beta1, while discovery-based selection is planned rather than already available.

So a PaaS should not turn “the cluster is on 1.37” into “every metrics client can use only v1.” Detect the served versions at runtime:

bash
kubectl get --raw /apis/metrics.k8s.io/ | jq .
kubectl get apiservice v1.metrics.k8s.io

The Kubernetes resource-metrics documentation also makes the deployment boundary explicit. Metrics API is served through the API aggregation layer by an implementation such as metrics-server or another compatible adapter. A cluster without a healthy APIService has no resource metrics, regardless of its Kubernetes version.

That distinction is useful for agent design: API stability removes one class of breakage; it does not remove dependency health checks.


Read the signal before trusting it

The resource metrics pipeline is a short chain, and every link affects an automated decision:

  1. The kubelet obtains node and container statistics from the runtime through the CRI or, where applicable, cAdvisor.
  2. Metrics-server collects those values from each kubelet and keeps a short-term in-memory view.
  3. The API server exposes the aggregated result through metrics.k8s.io.
  4. HPA, VPA, kubectl top, or your agent reads the result.

The pipeline documentation says this API provides only the minimum CPU and memory metrics needed for resource autoscaling and basic inspection. It is not a replacement for a full monitoring pipeline, and it cannot answer questions such as queue depth, request latency, error rate, or saturation in a database.

The values themselves also need interpretation. CPU is an average core usage calculated as a rate over a cumulative counter; the window field tells you the interval used for that rate. Memory is the working set at the instant of collection. Kubernetes documents working set as an estimate that can include cached file-backed memory and can vary with the host operating system.

Those semantics change what an agent should do. A 30-second CPU window is evidence about recent consumption, not a prediction of the next minute. A memory working-set increase is a reason to inspect pressure and restart behavior, not proof that the application has a leak. If the agent needs a prediction, it must say which additional time-series data or application metric supplies that prediction.

A worked replica calculation

Suppose a Deployment has four ready Pods. Each application container requests 500m CPU, and the target is 60% utilization, or 300m per Pod. The stable Metrics API returns these current CPU values over a 30-second window:

PodCPU usageCPU requestUtilization
api-0420m500m84%
api-1380m500m76%
api-2610m500m122%
api-3590m500m118%
Average500m500m100%

The basic HPA formula is:

text
desiredReplicas = ceil(currentReplicas × currentMetric / desiredMetric)
                = ceil(4 × 500m / 300m)
                = 7

This is not a recommendation to jump straight to seven. It is the unbounded mathematical recommendation before min/max replicas, tolerance, readiness, policy, and scheduling capacity are considered. The HPA algorithm documentation describes a 10% default tolerance, a default 15-second controller loop, and stabilization behavior that defaults to a five-minute downscale window. A platform may choose stricter rules for agent-initiated actions.

For example, if api-3 has no metric because metrics-server missed that kubelet, an agent must not silently average the three values and claim the result represents four Pods. Kubernetes HPA handles missing metrics conservatively: for a possible scale-up, it assumes missing Pods consume 0% of the target; for a possible scale-down, it assumes they consume 100%. Your agent can use the same conservative approach, or refuse to act until the set is complete. It must expose which choice it made.

The distinction between Pod and container metrics is important too. Summing all containers can hide a saturated application container behind a low-usage logging sidecar. Kubernetes supports container resource metrics in autoscaling/v2; a platform should preserve the selected container identity in the evidence it gives an agent rather than returning one opaque Pod total.


The platform contract around the API

The Metrics API does not know your product’s tenant model. A self-hosted PaaS has to add the boundaries that make the data actionable.

Scope the read and the write separately

An agent operating Tenant A’s service should receive only the namespace, workload, and Pod data that Tenant A is allowed to see. Its write permission should be narrower still: changing the target’s scale subresource within an approved min/max range, not editing the Deployment, HPA, ResourceQuota, or arbitrary workloads.

Kubernetes authorization is deny-by-default, and the API server evaluates namespace, resource, subresource, verb, and identity. The authorization reference notes that authorization happens before admission. Use that ordering deliberately: RBAC limits which object the agent may read or scale; admission validates the proposed object or scale request against policy.

A useful agent-facing response should therefore include both the data and the authorization context:

json
{
  "tenant": "team-a",
  "namespace": "team-a-prod",
  "workload": "api",
  "selector": "app=api",
  "podsExpected": 4,
  "podsObserved": 4,
  "metricsApiVersion": "metrics.k8s.io/v1",
  "samples": {
    "timestamp": "2026-08-31T18:40:00Z",
    "window": "30s",
    "maxAgeSeconds": 60
  },
  "scalePolicy": {
    "minReplicas": 2,
    "maxReplicas": 10,
    "maxChangePerAction": 2,
    "downscaleCooldownSeconds": 300
  },
  "can": ["get-metrics", "propose-scale"],
  "cannot": ["apply-scale"]
}

The last two fields illustrate a useful separation: an agent may calculate and explain a proposal, while a policy controller or human approval step performs the write. If you do grant apply-scale, record the identity and exact input used for the decision.

Treat freshness and completeness as safety inputs

Kubernetes gives you timestamp and window; it does not prescribe the maximum age at which your product should act. Pick that threshold per workload and serialize it alongside the sample. A simple starting policy for a 30-second collection window is:

  • reject a sample with a future timestamp;
  • reject a zero or missing window;
  • reject a sample older than 60 seconds at evaluation time;
  • require every expected ready Pod to have a sample for scale-down;
  • permit scale-up on partial data only if the proposal uses a documented conservative assumption;
  • stop acting when the Metrics APIService, metrics-server, or kubelet source is unhealthy.

The 60-second value is an example platform policy, not a Kubernetes guarantee. A batch worker with a ten-minute control loop and a latency-sensitive API should not share the same threshold. What matters is that the threshold is explicit, tested, and visible to the agent.

Readiness is another part of the evidence. The HPA controller sets aside Pods that are initializing or whose latest CPU sample predates readiness; its default initial-readiness delay is 30 seconds and its CPU initialization period is five minutes. An agent that sees a newly rolled-out Pod should receive that state, otherwise it may interpret startup behavior as steady-state load and make a second change while the first rollout is still converging.

Make capacity and quota part of the answer

Seven desired replicas are not useful if the cluster can schedule only four. Resource requests, not observed usage, determine much of the scheduler’s placement and quota accounting. ResourceQuota documentation describes per-namespace limits for requests.cpu, requests.memory, limits, storage, and extended resources such as GPUs. It also warns that a Deployment can be accepted while some of its Pods remain unscheduled.

Before applying a scale proposal, the platform should calculate the requested CPU and memory after the change, compare it with namespace quota and allocatable cluster headroom, and report unschedulable capacity as a first-class outcome. “Scaled to seven” should mean seven ready Pods, not merely a Deployment whose desired replica field says seven.


A small agent control loop that can be reproduced

The safest first version of agent autoscaling is intentionally boring:

  1. Discover. Check which Metrics API versions are served and whether v1.metrics.k8s.io is available.
  2. Resolve scope. Read the approved namespace, workload selector, expected Pod set, current scale, rollout state, and policy bounds.
  3. Fetch. Get PodMetrics from the stable endpoint where available, retaining timestamp, window, and per-container values. Keep v1beta1 as a compatibility fallback during migration.
  4. Validate. Reject stale, future, incomplete, unauthorized, or semantically mismatched samples. Record the reason instead of substituting zero.
  5. Calculate. Apply the HPA-style ratio to the selected metric and round up, then clamp to min/max and the per-action delta.
  6. Check capacity. Evaluate requests, quota, allocatable capacity, disruption budgets, rollout state, and cooldowns.
  7. Propose. Return the samples, formula, assumptions, expected result, and an idempotency key. Do not hide uncertainty in a prose summary.
  8. Apply and observe. If policy permits, write only the scale subresource. Wait for the resulting ready-Pod count and record whether the desired state converged.

An ordinary HPA remains a good baseline for the application-level control loop:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api
  namespace: team-a-prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 2
  maxReplicas: 10
  behavior:
    scaleUp:
      policies:
        - type: Pods
          value: 2
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60

An agent can explain why this HPA would eventually request seven replicas in the worked example, but it should not bypass the HPA and patch the Deployment simply because it can. If the product adds agent control, keep the proposal and apply paths separate and make the agent’s output machine-readable enough for another controller to verify.

This is also where a self-hosted PaaS differs from a dashboard. A dashboard optimizes for human investigation. An agent control surface must answer: which tenant, which Pods, which window, which policy, which capacity, which identity, and what happened after the write?


Stable data is the beginning of agent safety

Kubernetes v1.37 gives platform builders a durable resource-metrics API. That is valuable: clients can depend on metrics.k8s.io/v1, while implementations keep v1beta1 during the transition. But stable transport does not make an unsafe decision safe.

The actual platform contract is larger: scoped identity, complete samples, explicit freshness, correct metric semantics, readiness awareness, bounded writes, quota and capacity checks, and an audit trail. CPU and memory are enough to reproduce a basic HPA decision. They are not enough to claim that a service is healthy, that a queue is draining, or that a new replica will improve user-visible latency.

For a Cluster-API-backed PaaS, this separation is useful operationally. Cluster API can manage the machines and workload clusters; Kubernetes can expose the resource metrics; the PaaS can own the tenant policy and the agent-facing evidence contract. Each layer does one job, and an agent gets a narrow control loop instead of a cluster-admin-shaped prompt.

Before enabling an agent to change replicas, verify that it can answer these six questions from its structured input:

  • What exact Pods and containers produced the value?
  • How old is the sample, and over what window was CPU calculated?
  • Which Pods or metrics are missing, and what conservative assumption was used?
  • What is the permitted replica range and per-action delta?
  • Will the requested resources fit quota and schedulable capacity?
  • Which identity approved the write, and did ready capacity converge afterward?

If the answer to any question is “the agent can infer it from a graph,” the contract is not ready yet.

Bex.co is the open-source, AI-native Render alternative: push a git repository, get a running HTTPS service on machines you own, and give agents a narrow operational surface instead of unrestricted cluster access. Explore the Bex.co repository 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