Kubernetes GPU Sharing: MIG, Time-Slicing, and MPS (2026)

Kubernetes GPU Sharing: MIG, Time-Slicing, and MPS (2026)

Kubernetes GPU Sharing: MIG, Time-Slicing, and MPS in 2026

An H100 costs more per hour than a mid-tier engineer, yet in most clusters it spends its life mostly idle — a single inference replica, a stalled notebook, or a batch job that touches the tensor cores for 12% of the wall clock. The default Kubernetes GPU model made that waste structural: one nvidia.com/gpu request equals one whole physical device, indivisible, whether the pod needs 80 GB of HBM or 400 MB. Kubernetes GPU sharing is the set of mechanisms that break that one-to-one binding so several pods can land on the same card — and the central engineering fact is that every one of those mechanisms buys utilization by spending some amount of isolation. There is no free partition.

This is a reference architecture, not a tutorial. The goal is to give you a mental model precise enough to choose the right mechanism per workload, plumb it through the scheduler correctly, and predict how it fails.

What this covers: the three NVIDIA sharing primitives (time-slicing, MIG, MPS) and exactly how they differ; the Kubernetes plumbing that exposes them, from the legacy device plugin to Dynamic Resource Allocation; a real decision matrix; and the failure modes that bite in production.

Context and Background

For most of Kubernetes’ history, a GPU was an extended resource: an opaque integer advertised by the NVIDIA device plugin as nvidia.com/gpu: 1. The scheduler treated it exactly like it treats a countable widget — you either got a whole one or you got none. There was no notion of “40% of a GPU,” no memory dimension, no topology awareness. That model was fine in 2018 when a GPU node ran one training job. It aged badly the moment inference, notebooks, CI jobs, and fine-tuning runs started competing for the same expensive silicon.

The pressure is economic. Accelerators are the most expensive line item in an AI platform, and academic and vendor studies of production fleets have repeatedly found average GPU utilization sitting well below 50% — often far lower for interactive and inference workloads (treat specific percentages as fleet-dependent estimates, not universal constants). When a resource is both scarce and idle, packing multiple tenants onto it is the obvious lever. That is what raising GPU density means in practice, and it connects directly to the broader discipline covered in our guide to Kubernetes cost optimization and GPU right-sizing: sharing is one of the highest-leverage moves available, but only when the isolation cost is acceptable for the workload.

NVIDIA responded with three distinct hardware and driver features — time-slicing, Multi-Instance GPU (MIG), and Multi-Process Service (MPS) — and Kubernetes responded with two successive resource models: the device-plugin framework and, more recently, Dynamic Resource Allocation (DRA), which reached GA in Kubernetes v1.34 and is locked on by default in v1.35 and later. The rest of this post treats those two layers — the silicon feature and the orchestration plumbing — as separable concerns, because that is the only way to reason about them cleanly.

The Three Sharing Mechanisms as a Reference Architecture

Kubernetes GPU sharing works through one of three NVIDIA mechanisms: time-slicing context-switches the whole GPU between processes over time with no isolation; MIG carves the physical GPU into hardware-isolated instances with dedicated SMs and memory; MPS runs multiple process contexts concurrently and partitions the streaming multiprocessors spatially. They are not interchangeable settings of one feature — they are three different points on the isolation-versus-density curve, and choosing correctly starts with knowing which curve you are on.

Flowchart comparing kubernetes gpu sharing mechanisms time-slicing MIG and MPS

Figure 1: The three sharing mechanisms branch from a single physical GPU, each with a distinct isolation profile.

Figure 1 shows the branch. From one physical GPU you can reach time-slicing (time-division, no isolation, any card), MIG (hardware partitions, strong isolation, Ampere-class and newer), or MPS (spatial SM sharing, soft limits, weak fault isolation). Read the three leaf nodes as the three isolation contracts you can sign. Everything downstream — scheduler configuration, blast radius, quality-of-service — follows from which contract you pick.

Time-slicing: oversubscription with no walls

Time-slicing is the simplest and the most dangerous to use carelessly. The GPU’s context scheduler switches between CUDA contexts on a time basis, exactly like an operating system time-shares a CPU core between processes. Each pod gets the whole GPU for a slice of time, then yields. There is no memory isolation and no fault isolation: all sharing pods draw from the same HBM pool, so one process that allocates too much triggers an out-of-memory error that can take down its neighbours, and an illegal memory access or a GPU reset propagates to every context on the card.

In Kubernetes, time-slicing is configured through the NVIDIA device plugin’s sharing.timeSlicing block, which advertises a replicas count. Setting replicas: 4 makes one physical GPU appear to the scheduler as four allocatable nvidia.com/gpu units. The scheduler is not aware these are the same silicon; it simply sees four resources and packs four pods. That is the whole trick, and also the whole risk — the abstraction lies to the scheduler about isolation. Time-slicing is the right tool for development clusters, bursty low-criticality jobs, CI, and demo environments where a crashed neighbour is an annoyance rather than an incident.

MIG: hardware partitions with real walls

Multi-Instance GPU is the opposite philosophy. On A100, H100, H200, and Blackwell-class data-center GPUs, MIG physically partitions the die into up to seven instances, each with dedicated streaming multiprocessors, a dedicated slice of L2 cache, dedicated memory controllers, and a dedicated slice of HBM. The partitions are enforced in hardware, so a fault or memory blow-up inside one instance is contained — this is the only mechanism here that gives you true multi-tenant isolation on a single card.

Profiles are named by compute-slices and memory, for example 1g.10gb, 2g.20gb, 3g.40gb, and the full-card 7g.80gb on an 80 GB H100. The geometry is fixed at configuration time and not every combination is legal: a valid H100 layout is 3g.40gb + 2g.20gb + 1g.10gb + 1g.10gb, which sums to 7 compute-slices and 80 GB, while 4g.40gb + 4g.40gb is invalid because it exceeds the 7g compute budget. Memory bandwidth scales with the slice — a 1g.10gb instance sees a fraction of the card’s full bandwidth while a 7g.80gb instance gets essentially all of it. The cost of this rigor is inflexibility: changing the profile mix means draining the instances and reconfiguring, which is a disruptive operation, not a live knob.

MPS: concurrent contexts, spatial partitioning

CUDA Multi-Process Service sits between the two. MPS lets multiple processes submit work into a single shared GPU context and run their kernels concurrently rather than in serialized time slices, and it can partition the streaming multiprocessors spatially between clients. Because kernels from different clients actually overlap on the hardware, MPS typically extracts more real throughput from small kernels than time-slicing, which pays a context-switch tax on every turn.

MPS also supports per-client limits: you can cap the fraction of SMs (active thread percentage) a client may use and set pinned-memory limits, giving a soft form of resource control that time-slicing lacks. What it does not give you is MIG’s hardware fault isolation — clients share address space and a fault can still affect the shared context. MPS is the sweet spot for many-small-process inference serving where throughput matters and the tenants trust each other.

Kubernetes Plumbing: Device Plugin vs Dynamic Resource Allocation

The three mechanisms above are silicon features. Kubernetes has to advertise, schedule, and bind them, and that is where the orchestration layer lives. This is also where the biggest 2026 change is: the field is migrating from the counting-based device-plugin model to the far more expressive Dynamic Resource Allocation model.

Flowchart of the kubernetes gpu scheduling path from pod request through scheduler device plugin or DRA to kubelet and GPU

Figure 2: The GPU allocation path. A pod’s request flows through the scheduler, which consults either the device plugin’s advertised extended resources or a DRA ResourceClaim, then the kubelet binds the device on the node.

Figure 2 traces the path. A pod declares it needs a GPU. The kube-scheduler filters and scores nodes for one that can satisfy the request. On the winning node the kubelet, working with either the device plugin or the DRA driver, allocates a concrete device and the container runtime injects it into the container. The difference between the two models is entirely in how expressive that request and allocation can be.

The legacy model: extended resources and the GPU Operator

In the device-plugin model, the NVIDIA device plugin runs as a DaemonSet, discovers GPUs (or MIG instances, or time-sliced replicas) on each node, and advertises them to the kubelet as extended resources like nvidia.com/gpu or nvidia.com/mig-3g.40gb. Pods request an integer count of those resources. The NVIDIA GPU Operator automates the surrounding machinery — driver installation, the container toolkit, the device plugin, DCGM exporters for metrics, and MIG management — so you are not hand-rolling node configuration.

This model is battle-tested and still the default in most clusters. Its limits are structural: an extended resource is an opaque count with no parameters. You cannot say “give me a GPU with at least 24 GB free and compute capability 9.0” in the request itself; you approximate it with node labels, taints, and separately-named MIG resources. Sharing is bolted on by making one GPU advertise as several units, which — as with time-slicing’s replicas — means the scheduler cannot reason about what it is actually handing out.

Dynamic Resource Allocation: structured, expressive requests

DRA reframes GPUs the way Kubernetes already treats storage with PersistentVolumeClaims. Instead of requesting an integer, a pod references a ResourceClaim that describes what it needs through structured parameters, and a DRA driver (NVIDIA has moved dra-driver-gpu under Kubernetes SIGs) publishes ResourceSlices describing the devices each node actually has — product name, memory capacity, compute capability, driver version, MIG profile, and more. The scheduler matches claims to slices using those attributes and can express sharing and topology natively rather than through the extended-resource fiction.

DRA reached GA in Kubernetes v1.34, its feature gate is locked on from v1.35, and Kubernetes v1.36 (April 2026) shipped with DRA as a stable, default-on part of the API. Practically, this means a pod can request a GPU by capability and let the platform pick a MIG instance or a shareable device that satisfies it — the request finally carries enough information for the scheduler to make a correct decision. DRA does not replace the silicon mechanisms; it is a better front-end to all three, and it is the model to build new GPU platforms against in 2026. The kubelet and scheduler roles do not change conceptually — the scheduler still filters and scores, the kubelet still binds — but the vocabulary they share becomes rich enough to describe fractional and heterogeneous GPUs honestly.

The object model is worth internalizing because it mirrors storage, which most operators already know. A DeviceClass is the cluster-level template describing a category of device and the selectors that qualify it, analogous to a StorageClass. A ResourceClaim is the per-workload request, analogous to a PersistentVolumeClaim, and it can be templated per-pod or shared across pods. A ResourceSlice is what each node’s DRA driver publishes to advertise the concrete devices it holds and their attributes. The scheduler reads the claims and the slices together and computes an allocation that satisfies the structured constraints — for example “a device with at least 24 GB and compute capability 9.0” — rather than decrementing an opaque counter.

Two DRA capabilities matter specifically for sharing. First, structured parameters let a claim express how a device may be shared, so a single physical GPU can be legitimately handed to multiple claims under policy the scheduler understands, ending the extended-resource fiction where the scheduler could not see contention. Second, DRA carries topology and device-attribute information, so a scheduler can prefer GPUs on the same NVLink domain or avoid crossing NUMA boundaries — decisions the counting model simply could not represent. During migration the two models coexist: a node can advertise legacy extended resources and DRA ResourceSlices at once, which is what makes an incremental cutover feasible rather than a big-bang rewrite.

Deeper Analysis: Partition Geometry, Scheduling, and a Decision Matrix

The reference architecture is only useful if you can map a workload onto it. This section goes one level down: how a MIG card actually looks when partitioned, how the scheduler bin-packs fractional GPUs, and a side-by-side decision matrix.

Diagram of nvidia mig partitioning one H100 into 3g 2g and two 1g instances with dedicated SMs and memory

Figure 3: One physical H100 partitioned into four MIG instances of mixed sizes, each with its own SMs, L2 slice, and memory.

Figure 3 makes the MIG contract concrete. The physical H100 with its full SM count and L2 is carved into a 3g.40gb instance running a training job, a 2g.20gb instance doing batch inference, and two 1g.10gb instances each hosting a notebook. Each instance owns its SMs, its L2 slice, and its memory — the walls are real. Note the packing arithmetic: those four instances consume exactly 7 compute-slices and 80 GB, the full budget. This is why MIG planning is a bin-packing exercise done at configuration time, not a runtime autoscaling behaviour.

Scheduling and bin-packing fractional GPUs

Once GPUs are fractionalized — whether as MIG instances, time-sliced replicas, or DRA-shared devices — the scheduler’s job becomes bin-packing. You generally want a bin-packing scoring strategy (pack pods tightly onto already-used GPUs) rather than spread, so that whole GPU nodes can be freed and scaled down. This is exactly where GPU sharing composes with node autoscaling: a bin-packed cluster lets a scaler remove empty nodes, which is the mechanism our Karpenter node autoscaling deep dive explores in depth. Sharing raises per-node density; autoscaling turns that density into a smaller bill.

Two observability and control caveats matter here. First, GPU metrics come from DCGM (the Data Center GPU Manager exporter), which the GPU Operator can deploy; SM occupancy, memory used, and per-instance utilization are what you watch to know whether sharing is actually helping or just hiding contention. Second, autoscalers that key off metrics — HPA and KEDA — do not natively fractionalize a GPU. They can scale the number of replicas up and down, and those replicas can each request a fractional slice, but neither will carve a GPU for you; the fractionalization must already exist as an allocatable resource. Confusing “scale the replica count” with “give each replica less GPU” is a common design error.

Right-sizing the request, not just the node

Sharing is one half of efficiency; requesting the correct slice is the other. A pod pinned to a 7g.80gb MIG instance that only needs 8 GB is as wasteful as an un-shared card. The same right-sizing discipline that applies to CPU and memory applies to GPU slices, and the mechanics of adjusting requests without disruptive restarts connect to in-place pod resize and right-sizing — though note that GPU device allocations are generally not resizable in place today, so the lever is choosing the right slice up front rather than adjusting it live.

The decision matrix

The following matrix is the core artifact of this post. Read it row by row against your workload’s constraints.

Dimension Time-Slicing MIG MPS
Memory isolation None (shared HBM pool) Hard (dedicated HBM per instance) Soft (per-client limits, shared context)
Fault isolation None (crash/OOM affects neighbours) Hard (contained to instance) Weak (shared context, fault can propagate)
Granularity Coarse (equal time slices via replicas) Fixed profiles (1g/2g/3g/4g/7g) Fine (SM % and memory limits per client)
Hardware support Almost any NVIDIA GPU A100, H100, H200, Blackwell-class only Volta and newer
Failure blast radius Whole card Single instance Shared context
Concurrency model Time-division (serialized turns) Spatial (parallel, hardware-isolated) Spatial (parallel, shared context)
Reconfigure cost Cheap (change replicas) Expensive (drain + repartition) Moderate (restart MPS control daemon)
Best-fit workload Dev, CI, bursty, low-criticality Multi-tenant, SLA inference, mixed prod Many small trusted inference processes
Config complexity Low High (profile planning, GPU Operator) Medium (MPS daemon + limits)

The matrix encodes the central trade-off literally. Reading down the isolation rows, MIG is strongest and time-slicing weakest; reading down the flexibility and cost rows, the order inverts. MPS is genuinely in between on most dimensions, which is why it is easy to under-appreciate — it is neither the cheapest nor the safest, but for throughput-bound inference on trusted tenants it frequently wins on real work done per GPU-hour.

A worked capacity example

Numbers make the trade-off tangible. Consider a team running many small inference replicas, each of which genuinely needs about 10 GB of GPU memory and rarely exceeds a fraction of the card’s compute. The following figures are illustrative arithmetic, not a benchmark — the point is the shape of the reasoning, not the exact ratios.

On an 80 GB H100 with MIG, seven 1g.10gb instances fit, so one physical card serves seven isolated replicas. If each replica would otherwise have monopolized a whole card, that is a 7:1 improvement in served replicas per GPU, and — because MIG isolates — a noisy replica cannot starve the other six. The catch is stranded capacity: seven 1g.10gb slices consume all seven compute-slices but only 70 GB of the 80 GB, so 10 GB sits unusable under that geometry.

Time-slicing the same card with replicas: 7 also lets seven pods schedule, but now all seven share the full 80 GB HBM pool and the full compute with no walls. Density looks identical on paper, yet the risk profile is opposite: one pod that spikes to 30 GB can OOM the others. MPS on the same card would run the seven contexts concurrently with SM caps, likely delivering higher aggregate throughput than time-slicing’s serialized turns while keeping the shared-fault caveat.

The lesson from the arithmetic is that “how many pods fit” is the least interesting question. The interesting questions are what happens when one pod misbehaves and how much capacity the chosen geometry strands. A platform that reports 7:1 density but suffers weekly OOM cascades is worse than one that reports 6:1 with clean isolation.

Configuring each mechanism in practice

The configuration surface differs enough to shape rollout effort. Time-slicing is a ConfigMap: you define a sharing.timeSlicing policy with a replicas value in the device plugin’s config and optionally attach it to specific nodes with labels, so a g4 pool can advertise four replicas while an h100 pool stays whole. It is a one-line conceptual change and reversible in seconds, which is exactly why it is the default for dev clusters.

MIG requires more. The GPU must be switched into MIG mode (a driver-level operation that resets the device), then partitioned into a chosen geometry. The GPU Operator exposes named MIG configurations — a “single” strategy advertises one profile type per node, while a “mixed” strategy lets a node expose several profiles at once as distinct resources like nvidia.com/mig-1g.10gb and nvidia.com/mig-3g.40gb. Changing geometry drains the node’s GPU workloads, so MIG changes are planned maintenance, not runtime tuning.

MPS runs a control daemon per GPU that brokers the shared context; the GPU Operator can manage it, and clients set an SM percentage and pinned-memory limit through environment variables. The daemon is the single point that must stay healthy — if it dies, the shared clients lose their broker.

Trade-offs, Gotchas, and What Goes Wrong

The mechanisms fail in specific, recognizable ways. Knowing them ahead of time is the difference between a sharing rollout that sticks and one that gets ripped out after the first incident.

Time-slicing OOM cascades. Because there is no memory isolation, the classic failure is one pod allocating a large tensor and pushing the shared HBM over the edge, which surfaces as CUDA out-of-memory errors in other pods that did nothing wrong. This is maddening to debug because the victim and the culprit are different workloads. Never put anything you care about on a time-sliced GPU alongside untrusted or unbounded-memory neighbours.

MIG rigidity and stranded capacity. Fixed geometry means capacity gets stranded. If your card is partitioned into seven 1g.10gb instances and a job needs 20 GB, it cannot use two 10 GB instances as one — MIG instances are not poolable. Repartitioning to free a 2g.20gb requires draining the whole GPU. Teams that pick MIG for its isolation and then fight its inflexibility usually under-planned the profile mix. Plan the geometry against the actual workload histogram, and expect to leave some slices idle.

MPS’s shared-fault surprise. MPS looks like isolation because of its per-client SM and memory caps, but those are performance limits, not fault domains. A misbehaving client can still corrupt the shared context or trigger a fault that affects co-resident clients. Treat MPS tenants as a single trust domain, never as isolated tenants.

Scheduler fictions. Both time-slicing replicas and naive over-advertisement lie to the scheduler about isolation, so scheduler-level fairness and preemption do not understand contention on the physical card. This is precisely the problem DRA is designed to end, but until a cluster is on DRA the operator must compensate with node pools, taints, and careful workload placement.

Version and driver drift. MIG requires the GPU to be in MIG mode (a driver/BIOS-level toggle), MPS requires its control daemon running with the right environment, and DRA requires a recent Kubernetes and a matching driver. Mismatches produce confusing “resource not found” scheduling failures rather than clean errors.

Practical Recommendations

Start from the workload, not the mechanism. The decision flow below turns workload traits into a recommended sharing mode.

Decision flowchart mapping workload traits to recommended kubernetes gpu sharing mode

Figure 4: A decision flow from workload characteristics to a recommended sharing mechanism.

Figure 4 walks the questions in priority order: does the workload need hard isolation or multi-tenancy? If yes and the hardware supports MIG, use a right-sized MIG profile; if the hardware cannot do MIG, give a dedicated GPU or upgrade. If hard isolation is not required, is throughput on many small kernels the priority? Then MPS with compute limits. Otherwise, for dev, bursty, and low-criticality work, time-slicing with a sensible replicas count. The default answer for shared production inference on modern hardware trends toward MIG; the default for internal dev clusters trends toward time-slicing.

A short operating checklist:

  • Classify every workload by isolation need and criticality before assigning a mechanism. Mixed-criticality on one time-sliced card is an incident waiting to happen.
  • Deploy the GPU Operator and DCGM so you actually measure SM occupancy and per-instance utilization instead of guessing.
  • Plan MIG geometry against the real workload histogram, and accept some stranded slices as the price of isolation.
  • Adopt DRA for new platforms on Kubernetes v1.34+ — request GPUs by capability, not by opaque count.
  • Bin-pack, then autoscale. Pack fractional GPUs tightly so an autoscaler can retire empty nodes.
  • Right-size the slice, not just the node — a small job on a 7g instance wastes the whole card.

Frequently Asked Questions

What is the difference between MIG and time-slicing in Kubernetes?

MIG partitions a GPU in hardware into in

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *