Skip to main content

Dynamic Resource Allocation Comes of Age in Kubernetes 1.36: Scheduling GPUs Declaratively Instead of Renting a Managed Tier

9 min readDora NodaDora Noda
Share
On this page

Two GPU stories broke in the same season, moving in opposite directions. Kubernetes 1.36 shipped in April 2026 with Dynamic Resource Allocation's GPU-scheduling sub-features graduating in a batch — Prioritized List reached GA, and Partitionable Devices, Device Taints and Tolerations, and Device Binding Conditions all reached Beta. Three months later, Fly.io deprecated its entire GPU product line, effective August 1, 2026, while Google Cloud Run added per-second metered GPU billing to fill the gap. One platform is walking away from renting you a GPU. Kubernetes just made owning the scheduling logic for one noticeably less painful.

Here's the number that motivates the rest of this post: a Hetzner GEX131 (NVIDIA RTX PRO 6000 Blackwell, 96GB) run flat-out, 24/7, costs about €1.22/hour amortized over its €889/month lease — roughly half of Scaleway's on-demand H100 rate of €2.52/hour for the same continuous workload. Run that same card for four hours a day instead of 24, and the math flips hard: the flat lease costs €7.41 per hour actually used, while hourly billing on the identical box comes in at €1.42/hour. Whether owning the hardware wins depends entirely on utilization — and Kubernetes 1.36's DRA maturity is what finally lets a self-hosted fleet push utilization up by sharing one physical card across tenants instead of dedicating it to whichever workload got there first.

DRA itself isn't new in 1.36. The core API graduated to GA back in Kubernetes 1.34 (September 2025) and its feature gate locked on by default in 1.35. What 1.36 delivers is the layer above the core API maturing — the specific sub-features that make GPU scheduling usable in production, plus NVIDIA donating its DRA driver to the CNCF at KubeCon Europe 2026. That distinction matters, because it changes what a self-hosted platform can safely build on right now versus what's still catching up.

The problem DRA was built to replace

Kubernetes has scheduled GPUs the same way since the device plugin framework landed in Kubernetes 1.8, in 2017: a vendor daemon reports a bare count to the kubelet, which advertises it to the API server as an integer extended resource — nvidia.com/gpu: 4. Kubernetes' own docs are explicit that extended resources "are only supported as integer resources and cannot be overcommitted." A pod that asks for nvidia.com/gpu: 1 gets a GPU — the scheduler has no idea which one, how much VRAM it has, what MIG profile it supports, or what it's connected to over NVLink.

Every gap in that model gets patched the same way: hand-written node labels, taints, and affinity rules layered on top of scheduling logic that was never designed to reason about hardware attributes. A team wanting to reserve a specific GPU class for a specific tenant tier writes a taint and a toleration by hand. A team wanting to split one physical card across several lighter workloads configures NVIDIA's time-slicing or MIG setup out-of-band, invisible to the scheduler, and hopes nothing schedules onto a slice that's already spoken for. It looks like this in practice:

yaml
# The device-plugin-era workaround: reserve a tainted node,
# hope the label matches a GPU the scheduler can't actually see
apiVersion: v1
kind: Pod
spec:
  nodeSelector:
    gpu-type: rtx-pro-6000  # a label a human attached, not a scheduler fact
  tolerations:
    - key: "dedicated-gpu"
      operator: "Equal"
      value: "tenant-a"
      effect: "NoSchedule"
  containers:
    - name: inference
      resources:
        limits:
          nvidia.com/gpu: 1  # which GPU? how much VRAM? unknown to the scheduler

That's not a scheduler decision — it's a config-management decision wearing a scheduler's syntax. Dynamic Resource Allocation replaces the workaround with a declarative claim the scheduler can actually reason about.

What's concretely new for GPU scheduling in 1.36

Four sub-features move the model from "hope the label matches" to "the scheduler resolves the claim":

  • Prioritized List (GA) lets a claim express a fallback order — request an H100, and if none is free, accept an A100 — instead of a workload failing outright because its first-choice hardware is momentarily unavailable.
  • Partitionable Devices (Beta) gives DRA native support for carving a physical card into smaller instances, like NVIDIA's Multi-Instance GPU profiles, so a scheduler can allocate a fraction of a card the same way it allocates a fraction of a CPU core — instead of a hand-configured, scheduler-invisible MIG setup.
  • Device Taints and Tolerations (Beta) brings taint semantics down to the individual device, not just the node — a specific GPU can be reserved for a tenant tier or pulled out of scheduling for maintenance without taking its whole node offline.
  • Device Binding Conditions (Beta) lets the scheduler hold off committing a pod to a node until an external dependency — a driver finishing initialization, a device actually coming ready — is confirmed, closing the failure mode where a pod lands on a node whose GPU isn't actually usable yet.

The same declarative request from above, expressed through DRA, looks like this:

yaml
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
  name: gpu-inference
spec:
  selectors:
    - cel:
        expression: |
          device.attributes["gpu.nvidia.com"].memory.compareTo(
            quantity("40Gi")) >= 0
yaml
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
  name: inference-gpu-claim
spec:
  devices:
    requests:
      - name: gpu
        deviceClassName: gpu-inference

Nothing here names a node, a label, or a specific card. The claim states a requirement — at least 40GB of VRAM — and DRA's driver-published ResourceSlice objects tell the scheduler which physical devices, on which nodes, actually satisfy it. The scheduler resolves the claim the same way it resolves a CPU or memory request: by matching a declared need against advertised capacity, not by matching a label a human typed in.

What this buys a Cluster-API-managed fleet

For a self-hosted platform running GPU nodes under Cluster API rather than renting scheduling logic from a hyperscaler, these four features map directly onto the tenant-facing problems that used to require hand-rolled glue:

  • Fractional sharing for agent sandboxes. Partitionable Devices means a single GEX131's 96GB Blackwell card can be split into MIG instances and handed to multiple tenant AI-agent sandboxes concurrently, each with a hardware-isolated slice, instead of one sandbox monopolizing an entire physical card for a workload that only needs a fraction of it.
  • Per-tenant hardware reservation without node sprawl. Device Taints let an operator carve out specific physical GPUs for a premium tenant tier or a dedicated inference workload, at the device level, without needing a whole separate tainted node pool for every reservation tier.
  • Fewer failed first deploys. Device Binding Conditions close the same class of bug this list has flagged before with Kubernetes' Node Readiness Controller — a pod landing on hardware that reports available before it's actually ready — specifically for GPU driver initialization, which is exactly the kind of timing gap a freshly autoscaled GPU node hits.
  • Graceful degradation instead of hard failure. Prioritized List means a tenant's inference request can fall back from an unavailable top-choice GPU to an available second choice automatically, instead of the pod simply staying Pending.

None of this requires a platform to write its own device-plugin daemon, its own MIG-slicing automation, or its own binding-delay logic — it's now expressible as Kubernetes API objects that a Cluster-API-managed control plane already knows how to reconcile.

The economics: owned hardware vs. renting a metered tier

The headline number from the top of this post is worth walking through properly, because the answer changes with utilization, not with a fixed "self-hosting is cheaper" rule.

At full-time, continuous utilization (730 hours/month, a steady inference workload running around the clock):

OptionRateEffective monthly cost
Hetzner GEX131 (RTX PRO 6000 Blackwell, 96GB), flat lease€889/mo€889
Hetzner GEX131, hourly on-demand€1.4247/hr€1,040
Scaleway H100 (80GB), on-demand€2.52/hr€1,840

Run flat-out, the €889/month lease works out to about €1.22/hour — roughly half of Scaleway's on-demand H100 rate for the same continuous load, and cheaper than paying Hetzner's own hourly rate on the identical box. This is the case where owning (or long-leasing) the hardware wins decisively: a workload that never goes idle amortizes a fixed monthly cost down below anything billed per hour or per second.

At light, bursty utilization (roughly 4 hours/day, ~120 hours/month — closer to an agent-sandbox workload than a saturated inference service):

OptionRateMonthly cost at 120 hrs
Hetzner GEX131, flat lease (unused hours still billed)€889/mo€889
Hetzner GEX131, hourly on-demand€1.4247/hr€171
Scaleway H100, on-demand€2.52/hr€302
Cloud Run, NVIDIA L4 GPU, per-second (non-zonal)$0.0001867/sec (~$0.67/hr)$80 (€74)

At this utilization, the flat monthly lease is the worst option on the table — you're paying for 730 hours of hardware to use 120 of them. Hetzner's own hourly billing on the same Blackwell card is far cheaper, and Cloud Run's per-second L4 billing is cheaper still — though an L4 is a materially smaller GPU than a Blackwell RTX PRO 6000 or an H100, so that comparison only holds if the workload actually fits in an L4's memory and compute envelope. The honest takeaway: metered, per-second cloud billing is genuinely the better economic choice for a workload that's idle most of the day, unless Partitionable Devices lets that same physical Hetzner card serve several bursty tenants concurrently instead of one — at which point the effective utilization of the owned card rises without the platform needing five separate GPUs.

Fly.io's exit is the cautionary data point sitting underneath both tables: a metered-per-second rental model is also a product a vendor can simply discontinue. Fly's own postmortem on the decision put it plainly — "developers don't want GPUs... they want LLMs" — a demand signal too thin to keep a GPU line staffed and priced competitively. Whatever a metered tier costs today, "no line item for a workload" only holds as long as a vendor keeps deciding that workload is worth supporting. Hardware a platform leases or owns outright doesn't get deprecated out from under it on someone else's product roadmap.

What isn't ready to build on yet

Three of the four features covered here — Partitionable Devices, Device Taints, and Device Binding Conditions — are Beta, not GA, in Kubernetes 1.36. Beta means the API shape is unlikely to change drastically but isn't guaranteed stable the way Prioritized List's newly-GA status is. NVIDIA's DRA driver was only donated to the CNCF this year, at KubeCon Europe 2026; AMD has a DRA driver in progress but it's less mature, and Intel's is earlier still. Practically, that means a platform can and should start migrating GPU scheduling logic to DRA's declarative model now — the DeviceClass/ResourceClaim pattern above is safe to build tooling around — but shouldn't wire hard tenant SLAs to Beta-only features like device-level taints or MIG partitioning until they graduate. Build the plumbing on the stable core; treat the newer sub-features as an early-access capability you can turn on for internal or opt-in workloads first.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with a Cluster-API-managed fleet underneath it. Star the repo on GitHub or deploy your first app today.

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