Skip to main content

About Kueue

Kueue is a Kubernetes-native system that manages jobs using quotas. Kueue makes job decisions based on resource availability, job priorities, and the quota policies defined in your cluster queues. Kueue determines when a job waits for available resources, when a job starts (Pods created), and when a job is preempted (active Pods deleted). Use Kueue on CKS to prioritize batch, AI, and ML workloads, share GPU capacity fairly across teams, and reduce idle time on expensive accelerators. CKS supports Kueue by default. To simplify getting started, CoreWeave provides a Helm chart for installing Kueue. The cks-kueue chart also includes a kueue subchart, used to configure Kueue for deployment into your CKS cluster.
Always install Kueue with CoreWeave’s cks-kueue chart. Upstream Kueue registers admission webhooks that intercept system and infrastructure Pods, which can deadlock the cluster and prevent it from starting. The cks-kueue chart configures the webhooks to exclude these Pods, so the cluster can always start.
When you install Kueue through the CoreWeave Helm chart, Kueue metrics are automatically scraped and ingested into the Kueue Scheduling Dashboard in CoreWeave Grafana.

Usage

Install the cks-kueue Helm chart to deploy Kueue into your CKS cluster. The chart installs the Kueue controller and CRDs so you can begin defining queues and submitting workloads. Add the CoreWeave Helm repo so Helm can locate the cks-kueue chart.
Then, install Kueue on your CKS cluster.
After the chart installs, the Kueue controller runs in the kueue-system namespace and the Kueue CRDs are available for use in your cluster.

Sample Kueue configuration

After you install the cks-kueue chart, use the following sample configuration to set up a basic Kueue environment for CKS. This configuration creates a single shared queue that gets you started with a proof of concept. Production deployments usually divide capacity among teams instead. For more information, see Share capacity across teams with Cohorts. The samples use kueue.x-k8s.io/v1beta2. Before applying them, confirm that your installed Kueue CRDs serve this API version. The configuration includes the following Kueue components:
  • ResourceFlavor: Defines the characteristics of compute resources (CPU, memory, GPUs) available in your cluster.
  • ClusterQueue: Establishes resource quotas and admission policies across your entire cluster.
  • LocalQueue: Creates namespaced queues that reference a ClusterQueue for job submission.
  • WorkloadPriorityClass: Defines priority levels for jobs to determine scheduling order and preemption behavior.
The configuration also defines two priority classes for different job types: production jobs with high priority and development jobs with lower priority.
For more information, see Using Kueue with Ray on CKS.

Share capacity across teams with Cohorts

A Cohort groups ClusterQueues so that a team can borrow quota that other teams aren’t using. With the reclaimWithinCohort policy shown in the following sample, a team can reclaim its nominal quota by preempting eligible workloads in borrowing queues. Admission still depends on the workload’s resource requirements and scheduling constraints. A single ClusterQueue is enough for a proof of concept, but most organizations divide cluster capacity among several teams. You can give each team its own ClusterQueue with a nominal quota and a LocalQueue in the team’s namespace. Nominal quota defines an admission entitlement; it does not reserve physical GPUs. The following configuration reuses the previous sample’s ResourceFlavor and WorkloadPriorityClass resources. It splits the same resource quotas between two teams, team-a and team-b, and joins their ClusterQueues in a Cohort named org-cohort. It also creates the team namespaces and a default LocalQueue in each namespace.
Use these team queues instead of the previous sample’s shared queue. If you applied that sample, stop submitting jobs to its default LocalQueue in the default namespace. Wait for its workloads to finish, then remove that LocalQueue and cluster-queue before applying the team configuration. Otherwise, both configurations can admit work against the same physical capacity.
Submit each team’s jobs in its namespace with the label kueue.x-k8s.io/queue-name: default. To select a priority, also set kueue.x-k8s.io/priority-class to prod-priority or dev-priority on the job. Creating the priority classes alone does not assign them to jobs.
With this configuration, Kueue behaves as follows:
  • When team-b is idle, team-a has a quota ceiling of 12 GPUs: its 8 nominal GPUs plus 4 borrowed from the Cohort. Running those jobs also requires sufficient CPU, memory, RDMA resources, and physical capacity.
  • When team-b needs its nominal quota, Kueue can preempt eligible workloads in team-a’s borrowing queue to make room (reclaimWithinCohort: Any). Reclaim can select workloads regardless of their priority, provided the incoming workload can fit after preemption.
  • Within each queue, jobs with the prod-priority class can preempt jobs with the dev-priority class when needed for admission (withinClusterQueue: LowerPriority).
Kueue’s fair sharing feature supports withinClusterQueue and reclaimWithinCohort, but not borrowWithinCohort. If you enable fair sharing, remove borrowWithinCohort from your ClusterQueues.

Plan your ClusterQueue split

The right way to divide quota among ClusterQueues depends on your organization. When you plan the split, consider the following:
  • Create one ClusterQueue for each team that needs a nominal quota, and a LocalQueue in each namespace where that team submits jobs.
  • Set each nominalQuota to the team’s share. For a fixed-size cluster, keep the sum of quotas within the allocatable capacity of the Nodes that the ResourceFlavor matches, after accounting for system components and workloads outside Kueue. Kueue doesn’t validate configured quotas against actual Node capacity, although topology-aware scheduling checks physical fit during workload admission.
  • Use namespaceSelector to control which namespaces can submit jobs to each queue.
  • Use borrowingLimit and lendingLimit to bound how much capacity moves between teams. For more information, see ClusterQueue concepts in the Kueue documentation.
  • Define WorkloadPriorityClass resources and assign them to jobs for consistent priority ordering. With reclaimWithinCohort: Any, reclaim can preempt even higher-priority workloads in borrowing queues.
  • Revisit the split when you add Node capacity or when teams change. Adding Nodes does not automatically increase the nominal quotas configured in these samples. Update those quotas when you want the queues to admit more work.

Observability

This section describes how to monitor Kueue activity after the chart is installed. CoreWeave Grafana provides a Kueue Scheduling Dashboard that you can use to monitor your Kueue cluster.

Topology-aware scheduling

Topology-aware scheduling (TAS) lets Kueue improve scheduling decisions by considering the physical topology of your cluster’s Nodes. This is important for HPC, AI, and ML workloads, where network latency between Nodes can be a performance bottleneck. TAS can co-locate a job’s Pods to minimize communication overhead and maximize performance. The TopologyAwareScheduling feature in the Kueue controller is enabled by default. However, to use it, you must adjust some of the Kueue resources so that Kueue references the Node labels that describe your cluster’s topology. After the Helm chart is installed and the Kueue CRDs exist, choose one of the following topologies based on CKS Node labels for Kueue to use:
  • The infiniband topology is for instance types that are a part of InfiniBand fabrics. See About GPU instances to find InfiniBand connected instances.
  • The multinode-nvlink-ib topology extends the infiniband topology to also include instance types with rack-scale NVLink. See About GPU instances to find NVLink connected rack instances.
  • The hostname topology is for instance types without InfiniBand fabrics. It prevents Kueue from admitting workloads when total capacity is sufficient but fragmented across Nodes.
After you upgrade the Helm chart, the new Topology CRs appear in the cluster. The following example configuration is an adjustment of the preceding one. It demonstrates how to use the Topology resources by referencing them in ResourceFlavor resources, which are then used by ClusterQueue and LocalQueue resources. Each ResourceFlavor must select a disjoint set of Nodes. Use nodeLabels so that no Node matches more than one flavor. Overlapping selectors cause a Node to belong to multiple flavors, which leads to ambiguous quota accounting and unpredictable scheduling. Match each topologyName to a Node pool whose hardware actually provides that topology. For example, pair the infiniband topology with an InfiniBand connected pool like B200, the multinode-nvlink-ib topology with a rack-scale NVLink pool like GB200, and the hostname topology with pools that lack InfiniBand.
Referencing a topology whose labels are not present on the selected Nodes causes Kueue to fail to admit workloads for that flavor. Verify that every label in the Topology levels list exists on the Nodes selected by the flavor’s nodeLabels.

Example jobs with topology constraints

Kueue applies topology constraints to a Pod set, which is a group of Pods with the same specification in a Workload. Configure topology annotations on the Pod template, at spec.template.metadata.annotations for a Job or spec.replicatedJobs[].template.spec.template.metadata.annotations for a JobSet. For example, ds.coreweave.com/nvlink.domain selects the NVLink domain level. It doesn’t name a particular rack or specify how many slices to place in each rack. The selected level must exist in the Topology referenced by the workload’s ResourceFlavor. Choose a positive slice size no greater than the Pod set count. Use a size that evenly divides the count when you need equal-sized slices. Multiple slices can share a domain. With eight Pods and a slice size of two, possible domain counts include the following, depending on capacity and other constraints:
  • 8
  • 6+2
  • 4+4
  • 4+2+2
  • 2+2+2+2
The setting doesn’t select one of these layouts. Kueue 0.20 adds TASPartialSlices, enabled by default, which allows a smaller final slice for a single slice topology level. For example, eight Pods with a slice size of three form slices of 3+3+2. Check your installed version and feature gates before relying on partial slices. Evenly divisible sizes also work with older releases. The kueue.x-k8s.io/podset-slice-required-topology-constraints annotation expresses nested slice constraints as a JSON array of topology levels and sizes. It requires TASMultiLayerTopology and replaces the two slice annotations in the table. Don’t combine them. It describes grouping within a topology hierarchy, not exact counts for named racks. For the full list of annotations and their semantics, see the Kueue Topology Aware Scheduling user-facing APIs.

Four Pods on one leafgroup with the InfiniBand queue

This example schedules four Pods within a single leafgroup:
This example schedules four Pods within a single NVLINK domain for GB200 Nodes:

Eight Pods in slices of two

This Job keeps each pair of Pods within one NVLink domain while allowing the eight Pods to use multiple domains. It uses the gb200-local LocalQueue and multinode-nvlink-ib topology configured in this section. Replace [TRAINING-IMAGE] with your workload image. Each Pod requests all four GPUs on a GB200 Node, so the eight Pods require eight Nodes.
For rejected annotations or a workload waiting for topology capacity, see Match the message to a fix.

Place an exact number of Nodes in each rack

For a performance test that requires a specific layout, select the Nodes explicitly with a test label and a matching nodeSelector field. Slice annotations constrain groups of Pods but don’t express exact rack allocations. A slice size of one permits any layout without enforcing a particular one.

One Node in rack 1, three in rack 2, and four in rack 3

This example runs eight Pods on eight GB200 Nodes, with one Pod per Node. It uses the gb200-local LocalQueue and multinode-nvlink-ib topology configured in this section. Work with a cluster administrator who can label and taint Nodes. Each selected Node must match the gb200-flavor ResourceFlavor and have the following resources available:
  • Four GPUs.
  • 32 CPUs.
  • 256 GiB of memory.
  • One rdma/ib resource.
1

Identify the rack groups

Inspect the NVLink domain labels to identify the rack groups:
Select one schedulable Node from the first domain, three from the second, and four from the third.
2

Label the selected Nodes

Label only those eight Nodes. Replace each RACK_N_NODE_N placeholder with the selected Node’s name. Use a label value unique to this test. If you change layout-1-3-4, use the same value throughout the example.
3

Optional: Taint the selected Nodes

To block new Pods that lack a matching toleration, taint the selected Nodes:
This taint doesn’t evict existing Pods. The Job in the next step includes the matching toleration. Its nodeSelector field restricts placement to the labeled Nodes. The toleration alone doesn’t select Nodes. See Assigning Pods to Nodes and Taints and Tolerations.
4

Save the Job manifest

Save the following manifest as gb200-rack-placement.yaml. Replace TRAINING_IMAGE with your workload image.Each Pod requests all four GPUs of a GB200 Node. With exactly eight labeled Nodes, the eight simultaneous Pods occupy one Node each. The unconstrained topology annotation allows the selected rack layout while Kueue still checks quota and physical capacity. Don’t add required, preferred, or slice topology annotations to this Pod template.
gb200-rack-placement.yaml
5

Submit the Job

6

Verify the rack distribution

After Kueue admits the Job and all eight Pods are running, list their assigned Nodes:
Example output:
Example output
List the labeled Nodes and their NVLink domains:
Example output, with illustrative NVLink domain values rack-1, rack-2, and rack-3:
Example output
Match each Pod’s assigned Node to its NVLink domain. These outputs show one Pod in rack-1, three in rack-2, and four in rack-3, each on a different Node. Confirm this distribution before measuring performance. Pod indexes don’t determine which rack receives each Pod.
7

Clean up

If you added the test taint, remove it after the test finishes. Then remove the test label:
For help adapting this approach to your workload or cluster configuration, contact CoreWeave Support.
Last modified on November 4, 2026