This page describes optional DRA features for advanced use cases. Some of these features require support from the DRA driver. Each feature notes its maturity and the feature gate that enables it.
Kubernetes v1.36 [beta](enabled by default)Devices represented in DRA don't necessarily have to be a single unit connected to a single machine, but can also be a logical device comprised of multiple devices connected to multiple machines. These devices might consume overlapping resources of the underlying phyical devices, meaning that when one logical device is allocated other devices will no longer be available.
In the ResourceSlice API, this is represented as a list of named CounterSets, each of which contains a set of named counters. The counters represent the resources available on the physical device that are used by the logical devices advertised through DRA.
Logical devices can specify the ConsumesCounters list. Each entry contains a reference to a CounterSet and a set of named counters with the amounts they will consume. So for a device to be allocatable, the referenced counter sets must have sufficient quantity for the counters referenced by the device.
CounterSets must be specified in separate ResourceSlices from devices. Devices can consume counters from any CounterSet defined in the same resource pool as the device.
Here is an example of two devices, each consuming 6Gi of memory from a shared counter with 8Gi of memory. Thus, only one of the devices can be allocated at any point in time. The scheduler handles this and it is transparent to the consumer as the ResourceClaim API is not affected.
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: resourceslice-with-countersets
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 2
driver: dra.example.com
sharedCounters:
- name: gpu-1-counters
counters:
memory:
value: 8Gi
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: resourceslice-with-devices
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 2
driver: dra.example.com
devices:
- name: device-1
consumesCounters:
- counterSet: gpu-1-counters
counters:
memory:
value: 6Gi
- name: device-2
consumesCounters:
- counterSet: gpu-1-counters
counters:
memory:
value: 6Gi
Partitionable devices is controlled by the
DRAPartitionableDevices feature gate
in the kube-apiserver and kube-scheduler.
Kubernetes v1.37 [alpha](disabled by default)Device compatibility groups let a DRA driver declare which partitioned devices can be co-allocated on the same physical hardware. Without this feature, incompatible device combinations are only detected when the kubelet prepares the Pod on a node — resulting in a failed preparation. With compatibility groups, the scheduler rejects incompatible combinations at scheduling time, before any node-side work begins.
This is most useful for hardware that supports mutually exclusive operating
modes. For example, a GPU that can run in either MIG mode or vGPU mode: a
device in MIG mode and a device in vGPU mode cannot be co-allocated because
they consume overlapping physical resources in incompatible ways. By declaring
compatibilityGroups, the driver makes this constraint visible to the
scheduler.
This feature builds on partitionable devices: the
compatibilityGroups field lives on device.consumesCounters[] entries, which
only exist for partitionable devices. Both the DRADeviceCompatibilityGroups
and DRAPartitionableDevices feature gates must be enabled in the
kube-apiserver and kube-scheduler.
A driver defines a compatibilityGroups list for each
device.consumesCounters[] entry in a ResourceSlice. The list contains
at most 2 opaque string names that represent the operating mode or partition
type of that device on that particular counter set.
When the scheduler allocates multiple devices that draw from the same counter
set, it computes the intersection of their compatibilityGroups. Allocation
succeeds only if that intersection is non-empty — meaning every co-allocated
device shares at least one common group name. Devices drawing from different
counter sets are never compared against each other.
A device that declares no groups (an unset, nil, or empty list) is treated as a special case: it is only co-allocatable with other no-group devices on the same counter set. It is never co-allocatable with a device that declares one or more groups.
The constraint applies across all claims being allocated in a single scheduling cycle: if two claims each allocate a device from the same counter set, the cross-claim group intersection is also enforced.
Consider a GPU that can operate in either MIG mode or vGPU mode. The driver publishes two devices, each consuming 4 GiB from the same shared memory counter of 8 GiB. Based on counter capacity alone, both devices could be allocated together. Each device declares its operating mode as a compatibility group, making the two modes mutually exclusive:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-counters
spec:
nodeName: worker-1
pool:
name: gpu-pool
generation: 1
resourceSliceCount: 2
driver: gpu.example.com
sharedCounters:
- name: gpu-0-memory
counters:
memory:
value: 8Gi
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-devices
spec:
nodeName: worker-1
pool:
name: gpu-pool
generation: 1
resourceSliceCount: 2
driver: gpu.example.com
devices:
- name: gpu-0-mig
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: 4Gi
compatibilityGroups:
- mig
- name: gpu-0-vgpu
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: 4Gi
compatibilityGroups:
- vgpu
In this example:
gpu-0-mig belongs to the mig group.gpu-0-vgpu belongs to the vgpu group.If a Pod or PodGroup requests two devices from this pool, the scheduler checks
whether the two chosen devices share a common compatibility group on the
gpu-0-memory counter set. Since {"mig"} ∩ {"vgpu"} = ∅, the pair is
rejected — even though the counter set has enough memory for both. Both
requests can only be satisfied by two MIG devices (or two vGPU devices) from a
pool where such pairs exist.
consumesCounters[] entry may declare at most 2 group names.When the DRADeviceCompatibilityGroups feature gate is disabled (the default
for alpha), the kube-apiserver strips the compatibilityGroups field from any
new or updated ResourceSlice — unless the old object already had the field
set. The scheduler then treats devices in any pool that previously had grouped
devices as belonging to an incomplete pool and skips them entirely.
Only a non-empty list counts as the field being set: compatibilityGroups: null
and compatibilityGroups: [] are treated identically to omitting the field.
Devices with them behave exactly like devices with no groups — they do not
cause the scheduler to treat the pool as incomplete.
Device compatibility groups is controlled by the
DRADeviceCompatibilityGroups feature gate
in the kube-apiserver and kube-scheduler. The
DRAPartitionableDevices feature gate
must also be enabled.
Kubernetes v1.36 [beta](enabled by default)The consumable capacity feature allows the same devices to be consumed by multiple independent ResourceClaims, with the Kubernetes scheduler managing how much of the device's capacity is used up by each claim. This is analogous to how Pods can share the resources on a Node; ResourceClaims can share the resources on a Device.
The device driver can set allowMultipleAllocations field added in .spec.devices of ResourceSlice
to allow allocating that device to multiple independent ResourceClaims or to multiple requests within a ResourceClaim.
Users can set capacity field added in spec.devices.requests of ResourceClaim to specify the device resource requirements for each allocation.
For the device that allows multiple allocations, the requested capacity is drawn from — or consumed from — its total capacity,
a concept known as consumable capacity.
Then, the scheduler ensures that the aggregate consumed capacity across all claims does not exceed the device’s overall capacity.
Furthermore, driver authors can use the requestPolicy constraints on individual device capacities to control
how those capacities are consumed.
For example, the driver author can specify that a given capacity is only consumed in increments of 1Gi.
Here is an example of a network device which allows multiple allocations and contains a consumable bandwidth capacity.
kind: ResourceSlice
apiVersion: resource.k8s.io/v1
metadata:
name: resourceslice
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 1
driver: dra.example.com
devices:
- name: eth1
allowMultipleAllocations: true
attributes:
name:
string: "eth1"
capacity:
bandwidth:
requestPolicy:
default: "1M"
validRange:
min: "1M"
step: "8"
value: "10G"
The consumable capacity can be requested as shown in the below example.
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: bandwidth-claim-template
spec:
spec:
devices:
requests:
- name: req-0
exactly:
deviceClassName: resource.example.com
capacity:
requests:
bandwidth: 1G
The allocation result will include the consumed capacity and the identifier of the share.
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
...
status:
allocation:
devices:
results:
- consumedCapacity:
bandwidth: 1G
device: eth1
shareID: "a671734a-e8e5-11e4-8fde-42010af09327"
In this example, a multiply-allocatable device was chosen. However, any resource.example.com device
with at least the requested 1G bandwidth could have met the requirement.
If a non-multiply-allocatable device were chosen, the allocation would have resulted in the entire device.
To force the use of a only multiply-allocatable devices, you can use the CEL criteria device.allowMultipleAllocations == true.
When requesting multiple devices in a ResourceClaim, you can use the DistinctAttribute constraint to ensure that each allocated device has a different value for a specified attribute. This constraint was introduced with the consumable capacity feature.
The DistinctAttribute constraint is particularly useful when working with multiply-allocatable devices. It prevents the scheduler from allocating the same device multiple times within a single ResourceClaim, even when that device allows multiple allocations.
Beyond preventing duplicate allocations, this constraint helps optimize performance by ensuring devices are distributed based on their attributes. For example, you can use it to distribute devices across different NUMA nodes to optimize memory bandwidth and reduce contention.
Kubernetes v1.36 [beta](enabled by default)Starting in Kubernetes v1.36, DRA enforces fine-grained authorization checks for updates
to ResourceClaim status by using synthetic subresources and node-aware verbs.
For security hardening guidance, including RBAC examples for scheduler and DRA drivers, see Hardening Guide - Dynamic Resource Allocation.
For a step-by-step cluster administrator procedure, see Harden Dynamic Resource Allocation in Your Cluster.
Kubernetes v1.37 [alpha](disabled by default)In Dynamic Resource Allocation (DRA), the kubelet coordinates with a node-local
driver via gRPC to prepare allocated devices before container start
(NodePrepareResources) and to unprepare them upon Pod termination
(NodeUnprepareResources). While this setup is critical for node-local hardware
such as GPUs or FPGAs, some resources are managed entirely in the control plane
and require no node-local setup.
The optional node operations feature allows resource drivers to declare that
specific node-local gRPC operations can be skipped. When configured, the kubelet
bypasses driver lookup and gRPC calls for those devices, eliminating the need to
deploy and maintain empty node-local drivers on every worker node.
Driver authors can specify the skipNodeOperations field in
.spec.skipNodeOperations of a ResourceSlice. This field is a list of unique
strings specifying the node-local operations to bypass for all devices in that
slice.
Valid values are:
"NodePrepareResources": Skips NodePrepareResources gRPC calls. This value
cannot be specified unless "NodeUnprepareResources" is also listed (or "*"
is specified). This limitation avoids Pods getting stuck in Terminating if a
node-local plugin is missing, since the plugin is not checked during Pod
startup when preparation is skipped."NodeUnprepareResources": Skips NodeUnprepareResources gRPC calls."*": Skips all node-local resource operations.Here is an example of a ResourceSlice for a control-plane resource that skips all node-local operations:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: control-plane-resources
spec:
nodeName: worker-1
pool:
name: central-pool
generation: 1
resourceSliceCount: 1
driver: control-plane.example.com
skipNodeOperations:
- "*"
devices:
- name: virtual-device-1
When the Kubernetes scheduler allocates a device to a ResourceClaim, it copies
the skipNodeOperations list from the ResourceSlice into the allocation
result:
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
...
status:
allocation:
devices:
results:
- device: virtual-device-1
driver: control-plane.example.com
pool: central-pool
skipNodeOperations:
- "*"
When a Pod runs on a node, the kubelet reads the allocation results. If all
allocated devices for a given driver within a ResourceClaim skip a specific
operation, the kubelet completely bypasses calling that gRPC hook for that
driver.
Because the skipNodeOperations setting is copied from the ResourceSlice into
the ResourceClaim at allocation time, running Pods and active allocations
retain whatever setting was in place when they were scheduled.
If a driver's node operation requirements are updated in place (for example, changing from requiring node operations to skipping them), existing claims will still use the previous configuration. To avoid issues—such as terminating Pods hanging while waiting for a decommissioned node plugin—cluster administrators should ensure no active claims exist for a driver before altering its node operation requirements or removing node-local driver DaemonSets.
To prevent Pods from being scheduled onto nodes where the kubelet does not
support skipping DRA operations (which would cause the kubelet to fail while
waiting for a missing node plugin), this feature integrates with Node Declared
Features. When a
Pod uses a ResourceClaim with skipNodeOperations configured, the Kubernetes
scheduler verifies that the target node declares support for the
DRAOptionalNodeOperations feature in its .status.declaredFeatures before
scheduling the Pod.
Optional node operations is controlled by the
DRAOptionalNodeOperations
feature gate in the kube-apiserver, kube-scheduler, and kubelet.
Kubernetes v1.36 [alpha]DRA drivers can expose device metadata such as device attributes (PCI bus addresses or mdevUUID for mediated devices) or network configuration directly to containers as JSON files. This lets applications inside the container discover information about allocated devices without querying the Kubernetes API or building custom controllers.
KEP-5304 defines a device metadata protocol that drivers must follow so applications inside the container see a consistent layout across drivers and clusters. The DRA kubelet plugin library implements this protocol for you; the rest of this section describes how to use it.
Device metadata follows the same rules as device access: it is available inside a container only when that container requests the device in its container specification, and not otherwise. For how to request DRA devices in Pods and containers, see Request devices in workloads using DRA.
The protocol consists of four rules:
File paths. Metadata files live inside containers under
/var/run/kubernetes.io/dra-device-attributes. For a directly referenced
ResourceClaim the path is
resourceclaims/<claimName>/<requestName>/<driverName>-metadata.json; for a
claim created from a ResourceClaimTemplate the path is
resourceclaimtemplates/<podClaimName>/<requestName>/<driverName>-metadata.json
(where podClaimName is pod.spec.resourceClaims[].name).
In cases where the ResourceClaim request uses the
prioritized list feature, only the top-level request
name is used for the <requestName> segment in the file path (that is,
the /<subrequest> portion is dropped). Inside the
JSON file, the requests[].name field carries the full
<request>/<subrequest> reference (for example, gpu/high-memory) so
that consumers can identify which alternative was allocated.
The path constants are defined in
k8s.io/dynamic-resource-allocation/api/metadata.
JSON API. Each file is a stream of one or more
DeviceMetadata
objects serialized as versioned JSON with apiVersion and kind, following
Kubernetes API conventions. The same metadata is encoded once per supported
API version (newest first). All objects in the stream are semantically
equivalent; consumers should use the first object they can decode.
Generation. When a driver updates a metadata file the embedded
metadata.generation field must increase so consumers can detect changes.
Container exposure. Files are typically exposed via CDI bind-mounts, but other mechanisms are permitted as long as the file appears at the correct path and is read-only inside the container.
Device metadata is a driver-side feature that does not require any Kubernetes
API changes or feature gates. Using the DRA kubelet plugin library is a common
way to implement a driver, but drivers can be built in other ways as well.
Drivers that use the kubelet plugin enable this feature by passing the
EnableDeviceMetadata and MetadataVersions
options
when starting the plugin. MetadataVersions specifies which API versions are
serialized into the metadata file and must be set explicitly by the driver.
Check the documentation of your DRA driver to learn whether device metadata is
supported and how to enable it.
When device metadata is enabled, the driver generates metadata files and CDI bind-mount specifications while preparing the allocated devices for the pod, before the consuming containers start. The metadata appears inside containers at the well-known paths as defined above.
When a single request allocates devices from multiple DRA drivers, each driver
writes its own metadata file. Containers enumerate *-metadata.json files in
the request directory to discover all devices.
The Go package
k8s.io/dynamic-resource-allocation/devicemetadata
provides utilities for reading and decoding these metadata files by applications
inside the container.
Each metadata file conforms to the
DeviceMetadata
API (metadata.resource.k8s.io/v1alpha1).
The following example shows a metadata file for a GPU device allocated through
a ResourceClaimTemplate:
{
"kind": "DeviceMetadata",
"apiVersion": "metadata.resource.k8s.io/v1alpha1",
"metadata": {
"name": "pod0-gpu-2kqrd",
"namespace": "gpu-test1",
"uid": "c7e7b22e-239b-4498-b27c-7f1344481e14",
"generation": 1
},
"podClaimName": "gpu",
"requests": [
{
"name": "gpu",
"devices": [
{
"driver": "gpu.example.com",
"pool": "worker-0",
"name": "gpu-0",
"attributes": {
"driverVersion": {
"version": "1.0.0"
},
"index": {
"int": 0
},
"model": {
"string": "LATEST-GPU-MODEL"
},
"uuid": {
"string": "gpu-18db0e85-99e9-c746-8531-ffeb86328b39"
}
}
}
]
}
]
}
Drivers provide metadata in one of two ways:
metadata.generation so consumers can detect changes. The MetadataUpdater
API in the DRA kubelet plugin library handles generation bookkeeping
automatically for driver authors.In both cases, metadata remains available to each consuming container for the lifetime of that container. Metadata files are cleaned up after all containers in the Pod have terminated.
To learn how to use device metadata in your workloads, see Access DRA device metadata.
Custom, hand-crafted drivers that do not use the DRA kubelet plugin library
must implement the device metadata protocol
themselves. That means writing DeviceMetadata JSON at the correct file paths,
incrementing metadata.generation on every update, and exposing the files
read-only inside the container through CDI or an equivalent mechanism.