On September 15, 2026, Kubernetes graduated Pod-Level Resource Managers to beta. If you run sidecars next to latency-sensitive app containers — a service mesh proxy, a log shipper, a metrics exporter — this is the feature that stops each of those lightweight helpers from demanding a whole exclusive CPU just so your main container can keep its NUMA-aligned cores.
Here is the deal in one table. Take a typical tenant pod: an app container that wants 2 exclusive CPUs, plus a mesh proxy and a log shipper that each idle near zero but spike under load.
| Before (per-container allocation) | After (pod-level managers, pod scope) | |
|---|---|---|
| App container | 2 exclusive CPUs | 2 exclusive CPUs, NUMA-aligned |
| Mesh proxy sidecar | 1 exclusive CPU (integer required) | shares a 1-CPU pod pool |
| Log shipper sidecar | 1 exclusive CPU (integer required) | shares a 1-CPU pod pool |
| Pod total reserved | 4 CPUs | 3 CPUs |
| QoS class | Guaranteed (all-or-nothing) | Guaranteed (pod budget is the determinant) |
One CPU back per pod. Across a fleet running hundreds of sidecar-heavy pods, that is real capacity — cores you already pay for on machines you already own, currently pledged to sidecars that spend most of their time idle. This post works through why the old model forced that waste, how the new hybrid allocation works, what changed between alpha and beta, and what a self-hosted platform should actually do about it.
Why per-container allocation wastes capacity
The kubelet's CPU Manager, Memory Manager, and Topology Manager historically made every placement decision at container granularity. That was fine when a pod was one container. It became a tax the moment sidecars became the default shape of a production pod.
The trap was all-or-nothing. To get exclusive, NUMA-aligned CPUs for your main application container, you had to assign integer CPU requests (equal to limits) to every container in the pod — including the logging agent and the telemetry exporter that would be perfectly happy sharing a core. If any container fell short of that bar, the pod forfeited the Guaranteed QoS class entirely, and with it the exclusive placement and NUMA alignment the main workload actually needed.
So operators faced a choice that was not really a choice: over-provision every sidecar with a dedicated physical core it would barely touch, or give up predictable performance for the workload that justified the hardware in the first place. On owned bare metal, where there is no cloud bill to absorb the slack but also no spare capacity hiding behind an autoscaler, that padding comes straight out of how many tenants fit on a node.
How the hybrid model works
Pod-Level Resource Managers (KEP-5526, alpha in v1.36, beta in v1.37) teach the kubelet's three managers to honor pod-level resource declarations — .spec.resources, the pod-wide CPU and memory budget from KEP-2837 — when making hardware placement decisions. When a pod declares that budget, it becomes the sole determinant of the pod's QoS class, and the managers carve it up in a hybrid fashion: exclusive slices for the containers that qualify, a shared pool for everything else.
A container still qualifies for exclusive allocation the familiar way: requests equal to limits for CPU and memory, with an integer CPU request. What changed is that the containers that do not qualify no longer sink the whole pod. They land in a shared pool instead, and which shared pool depends on the Topology Manager scope:
- Pod scope: the kubelet performs one NUMA alignment based on the entire pod's budget. Qualifying containers get exclusive CPU and memory slices from that NUMA node, and the leftover forms a pod shared pool. Sidecars in that pool share with each other but stay strictly isolated from the exclusive slices and from the rest of the node — NUMA-local, protected from neighbor interference, without consuming dedicated cores.
- Container scope: each container is evaluated individually. The container that needs it gets exclusive NUMA-aligned placement; a sidecar that does not can run in the general node-wide shared pool. Total consumption stays bounded by the pod limits either way.
Isolation follows the allocation. Containers with exclusive slices get CPU CFS quota enforcement disabled at the container level, so the Linux scheduler never throttles them. Shared-pool containers get CFS quotas enforced at the pod level, so the sidecars as a group cannot eat past the leftover budget.
The canonical example from the upstream announcement makes the shape concrete — a latency-sensitive database with a metrics exporter and a backup agent as native sidecars:
apiVersion: v1
kind: Pod
metadata:
name: tightly-coupled-database
spec:
# Pod-level resources establish the overall budget and NUMA alignment size.
resources:
requests:
cpu: "8"
memory: "16Gi"
limits:
cpu: "8"
memory: "16Gi"
initContainers:
- name: metrics-exporter
image: metrics-exporter:v1
restartPolicy: Always
- name: backup-agent
image: backup-agent:v1
restartPolicy: Always
containers:
- name: database
image: database:v1
# This Guaranteed container gets an exclusive 6 CPU slice from the pod's budget.
# The remaining 2 CPUs and 4Gi memory form the pod shared pool for the sidecars.
resources:
requests:
cpu: "6"
memory: "12Gi"
limits:
cpu: "6"
memory: "12Gi"Before this feature, those two sidecars would each have needed integer CPU requests to preserve the pod's Guaranteed status — two more exclusive cores pledged to a metrics exporter and a backup agent. Now the database keeps its 6 exclusive NUMA-aligned cores and the sidecars split the remaining 2 CPUs and 4 GiB in the pod shared pool.
What is new in beta
The September 15 beta announcement, from Google's Kevin Torres Martinez, is deliberately narrow — this graduation is about operational hardening, not a redesign. Two things changed since the May alpha:
PodResources API reporting. The kubelet's node-local PodResources gRPC service gained top-level cpu_ids and memory fields on its responses. Node-local monitoring agents and device plugins can now query a pod's exclusive assignments directly instead of reconstructing them from container-level allocations — and, critically, without double-counting. If you run per-node telemetry that sums CPU assignments, these are the fields that keep your dashboards honest once hybrid pods land.
Forward-compatible checkpoints. The alpha had a sharp edge: enabling the feature in 1.36 rewrote the kubelet's internal cpu_manager_state and memory_manager_state checkpoint files into a V3 format that older kubelets cannot parse. Downgrading a 1.36 kubelet after active use meant the kubelet failed to start until you drained the node, deleted the checkpoint files, and restarted. In 1.37 the checkpoints use a forward-compatible format, so downgrades no longer brick the kubelet — though a 1.36 kubelet still will not restore active pod-level assignments. Test your rollback path anyway; "forward-compatible format" is better than "delete state and restart," not better than rehearsing.
One timeline note worth keeping straight: the feature graduating here is the managers (KEP-5526). The underlying pod-level resource API itself — .spec.resources, KEP-2837, beta since v1.34 — went stable in the same v1.37 "Garhwal" release that shipped August 26 with 67 enhancements. The budget primitive is GA; the NUMA-aware carving of that budget is beta and still disabled by default behind the PodLevelResourceManagers feature gate. That split matters for planning: you can start modeling pod budgets against a stable API today while trialing the exclusive-placement behavior in staging.
What this means for packing tenants onto shared nodes
The win lands unevenly — and the unevenness is the point.
Single-container tenant pods gain almost nothing. If a tenant's deploy is one app container with no sidecars, there is no sharing opportunity: the pod budget equals the container reservation, and the managers have nothing to re-carve. A git-push PaaS whose typical workload is still a lone web container should not expect a capacity miracle from this feature.
Sidecar-heavy pods reclaim the padding. The win scales with sidecar count. A pod running app plus proxy plus log shipper — the shape this post opened with — drops from 4 reserved CPUs to 3. Add a metrics exporter and the before/after gap widens further, because every integer-CPU sidecar the old model forced is a core the new model returns to the schedulable pool. The same arithmetic applies to memory. If your platform is moving toward sidecars — a deploy agent, a log streamer, a mesh proxy injected per tenant — this feature decides whether that roadmap costs you a core per helper or a slice of a shared pool.
There is a NUMA nuance worth one paragraph. The alignment half of this feature pays off most on multi-socket bare metal, where landing all of a pod's containers on one NUMA node measurably cuts memory latency. On smaller single-socket nodes — a common shape in a Hetzner-weight fleet — alignment is trivially satisfied and the exclusive-CPU half (no CFS throttling for the hot container) carries the benefit. The reservation-efficiency win holds on both; only the latency story varies with hardware.
Observability arrives with the feature, not after it. Three kubelet metrics ship behind the gate: resource_manager_allocations_total and resource_manager_allocation_errors_total, both labeled with whether the allocation came from the pod or node pool, and resource_manager_container_assignments, which breaks containers into node_exclusive, pod_exclusive, and pod_shared. For a platform team, that last metric is the fleet-level view: the ratio of pod_shared to pod_exclusive tells you how much of your sidecar fleet has actually moved onto the new model versus how much is still burning whole cores.
So: trial the managers in staging now that beta has landed, model pod budgets against the now-stable .spec.resources API, and keep any tenant-facing rollout waiting on the beta maturing further while the feature stays opt-in. The question the alpha posed — "is it mature enough to test against our own sidecar-based features?" — now has a cleaner answer: the API is stable, the placement behavior is beta, and the downgrade trap that made even testing scary is fixed.
Enablement checklist and gotchas
If you are taking this to staging, the enablement list is five items, all kubelet-side:
- Enable the
PodLevelResourcesandPodLevelResourceManagersfeature gates. - Set a Topology Manager policy other than
none(best-effort,restricted, orsingle-numa-node). - Set the Topology Manager scope to
podorcontainerviatopologyManagerScopeinKubeletConfiguration. - Configure the CPU Manager with the
staticpolicy. - Configure the Memory Manager with the
Staticpolicy.
And the limits, from the official docs: only the static policies are implemented (no BestEffort memory policy), and the feature is Linux-only — on Windows nodes the managers are a no-op for pod-level allocations. If your fleet mixes operating systems, scope the trial to Linux node pools and keep Windows tenants on container-level reservations.
The remaining gotcha is the one beta fixed but alpha testers may still carry: any node that ran the 1.36 alpha with the gate enabled has V3 checkpoint files. Roll those nodes forward to 1.37 rather than back, and if you must roll back, follow the documented procedure — drain, delete the manager state files, restart the kubelet — instead of discovering the startup failure during an incident.
The pod is becoming the unit of resource thinking
Step back and the direction is clear. Pod-level resource declarations went stable. In-place pod-level resize reached beta in 1.36. Now the node managers that hand out physical hardware think in pods too. Kubernetes spent a decade allocating CPU and memory per container and deriving the pod as a sum; the 1.36–1.37 cycle inverts that, making the pod budget primary and the container slice derived. For performance-sensitive workloads on owned hardware — exactly the fleet a self-hosted PaaS runs — that inversion turns sidecars from a capacity tax into a rounding error.
Start with one staging node pool, one sidecar-heavy workload, and the pod_shared metric. The cores you get back are the cheapest capacity you will add this year.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



