GPU sharing with Multi-Process Service
NVIDIA Multi-Process Service (MPS) lets multiple containers run their CUDA work on one physical GPU concurrently, rather than in turns. An MPS control daemon funnels the work from each client into the GPU at the same time, and you can cap how much of the GPU each client may use through an active-thread percentage and a pinned-memory limit.
Use MPS when you want several cooperating workloads to make forward progress on the same GPU at once and you want coarse-grained control over how they share compute and memory. If you instead need workloads to take turns on an idle GPU with no configuration, refer to Time-slicing.
Feature status
MPS is controlled by the MPSSupport feature gate, an Alpha feature that is
disabled by default.
| Feature gate | Stage | Default |
|---|---|---|
MPSSupport | Alpha | false |
See the Feature gates reference for all available gates and their constraints.
Prerequisites
- The DRA Driver for NVIDIA GPUs must be installed. See Installation.
- The
MPSSupportfeature gate must be enabled. See Enabling the feature. MPSSupportcannot be enabled at the same time asDynamicMIG. These combinations are mutually exclusive.- To use multi-user mode (
multiUser: true), the GPUs must be Volta architecture or newer.
Enabling the feature
Enable the MPSSupport feature gate with helm upgrade:
helm upgrade dra-driver-nvidia-gpu oci://registry.k8s.io/dra-driver-nvidia/charts/dra-driver-nvidia-gpu \
--namespace dra-driver-nvidia-gpu \
--set featureGates.MPSSupport=true
The GPU kubelet plugin and webhook must both restart for the change to take effect. The rolling update happens automatically when you upgrade the Helm release.
How MPS works
When a pod that references an MPS-configured claim is scheduled, the GPU kubelet plugin:
- Sets the compute mode of the allocated GPU to
EXCLUSIVE_PROCESS. - Starts a dedicated MPS control daemon for the claim — a Deployment named
mps-control-daemon-<id>in the DRA Driver’s namespace, runningnvidia-cuda-mps-controlon the same node as the workload. - Applies the settings from
mpsConfig(active-thread percentage and pinned-memory limits) to that daemon. - Injects the MPS pipe and shared-memory directories into each container that shares the claim, so their CUDA processes connect through the daemon.
One control daemon is created per unique set of devices in a claim, and it is removed when the claim is released.
Because MPS sets the GPU compute mode to EXCLUSIVE_PROCESS and time-slicing
requires DEFAULT, MPS and time-slicing cannot be active on the same physical
GPU at the same time. Both strategies can be used elsewhere in the same cluster.
MPS configuration reference
MPS settings are provided under sharing.mpsConfig in a GpuConfig:
| Field | Type | Description |
|---|---|---|
defaultActiveThreadPercentage | integer (0–100) | Portion of the GPU’s threads made available to each MPS client. |
defaultPinnedDeviceMemoryLimit | quantity (e.g. 10Gi) | Pinned device-memory limit applied to every device in the claim. Must be at least 1 MB. |
defaultPerDevicePinnedMemoryLimit | map of device index or UUID to quantity | Per-device pinned-memory limit that overrides defaultPinnedDeviceMemoryLimit for the listed devices. |
multiUser | boolean | Runs the control daemon in multi-user mode so processes from different UIDs can share it. Defaults to disabled. Requires Volta or newer. |
All fields are optional. If strategy: MPS is set with no mpsConfig, the
driver starts a control daemon with default settings.
MPS example
This example shares one GPU between two containers in the same pod, capping each MPS client at 50% of the GPU’s threads and 10 GiB of pinned device memory.
Create a ResourceClaimTemplate
A ResourceClaimTemplate defines the GPU request and its MPS configuration.
Multiple pods can reuse the same template: Kubernetes automatically creates one
ResourceClaim per pod from it, and deletes that claim when the pod terminates.
Create a file called
mps-gpu.yaml:apiVersion: resource.k8s.io/v1 kind: ResourceClaimTemplate metadata: namespace: mps-example name: shared-gpu spec: spec: devices: requests: - name: mps-gpu exactly: deviceClassName: gpu.nvidia.com config: - requests: ["mps-gpu"] opaque: driver: gpu.nvidia.com parameters: apiVersion: resource.nvidia.com/v1beta1 kind: GpuConfig sharing: strategy: MPS mpsConfig: defaultActiveThreadPercentage: 50 defaultPinnedDeviceMemoryLimit: 10GiThe
deviceClassName: gpu.nvidia.comis required — it selects a full GPU. See MPS configuration reference for the availablempsConfigfields.Apply the manifest:
kubectl create namespace mps-example kubectl apply -f mps-gpu.yamlExample output:
resourceclaimtemplate.resource.k8s.io/shared-gpu created
Create a Pod that references the ResourceClaimTemplate
Reference the ResourceClaimTemplate by name in pod.spec.resourceClaims.
Kubernetes creates one ResourceClaim per pod when it is scheduled.
To share a single GPU across containers in the same pod, each container
references the same request name (request: mps-gpu) within the claim. If
containers referenced different request names, each would receive a separate
GPU.
Create a file called
mps-pod.yaml:apiVersion: v1 kind: Pod metadata: namespace: mps-example name: mps-pod spec: containers: - name: mps-ctr0 image: <your-image> resources: claims: - name: shared-gpu request: mps-gpu - name: mps-ctr1 image: <your-image> resources: claims: - name: shared-gpu request: mps-gpu resourceClaims: - name: shared-gpu resourceClaimTemplateName: shared-gpu tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule"Key fields:
image— replace<your-image>with your workload container image.resourceClaimTemplateName— must match the name of theResourceClaimTemplateyou created in the previous step.request: mps-gpu— must match the request name defined in the template. Both containers using the same value is what causes them to share one GPU through MPS.- Toleration — allows the pod to schedule on nodes that have the
nvidia.com/gpu: NoScheduletaint, which is common on GPU nodes. Remove it if your cluster does not use this taint.
Apply the manifest:
kubectl apply -f mps-pod.yamlExample output:
pod/mps-pod created
Verify MPS is active
Confirm the pod is running and both containers are ready:
kubectl get pod -n mps-example mps-podExample output:
NAME READY STATUS RESTARTS AGE mps-pod 2/2 Running 0 30sConfirm both containers see the same GPU:
kubectl exec -n mps-example mps-pod -c mps-ctr0 -- nvidia-smi -L kubectl exec -n mps-example mps-pod -c mps-ctr1 -- nvidia-smi -LBoth commands return the same GPU UUID, confirming the containers share one device.
Confirm the MPS control daemon is running in the DRA Driver’s namespace:
kubectl get deployment -n dra-driver-nvidia-gpu -l appYou should see a Deployment named
mps-control-daemon-<id>for the claim.On the GPU node, run
nvidia-smion the host. Processes routed through MPS appear with theM+C(MPS + Compute) process type, rather than the plainC(Compute) type used by non-MPS processes.
Limitations and considerations
- Isolation between clients is limited to the configured active-thread percentage and pinned-memory limits. There are no hard throughput guarantees.
- MPS requires the
EXCLUSIVE_PROCESScompute mode; time-slicing requiresDEFAULT. MPSSupportcannot be enabled together withDynamicMIG.- MPS claims cannot be prepared when
ConsumableSharesis enabled with a non-disabledconsumableSharesmode. - Setting
multiUser: truerequires GPUs of Volta architecture or newer; otherwise the claim fails to prepare.