A distributed training run with 32 workers can fail before its first gradient step. Eight GPU Pods land immediately, six wait for capacity, and the rest scatter across nodes with the wrong topology. The early arrivals then reserve expensive hardware while doing nothing useful. Conventional Pod scheduling did exactly what it was asked to do. That is the problem.
Workload-aware gang scheduling with Kubernetes v1.37 changes that contract. Rather than treating every Pod as an isolated scheduling decision, it lets a platform declare that a collection of Pods must start coherently—or remain pending together. For AI/ML training, MPI-style simulations, tightly coupled batch processing, and coordinated multi-Pod services, this isn’t ornamental metadata. It is an admission-control boundary.
Kubernetes v1.37 introduces this model through the Beta Workload and PodGroup APIs, plus Alpha CompositePodGroup support for scheduling trees. Pair that with Dynamic Resource Allocation (DRA) for GPUs on Amazon EKS and the platform primitive gets much clearer: allocate healthy devices, reserve a coherent group, bind it under an explicit policy, then expose the result as observable API state.
Why Kubernetes v1.37 gang scheduling exists
The Kubernetes v1.37 release blog, “Kubernetes v1.37: Advancing Workload-Aware Scheduling”, draws a useful distinction: a workload’s policy is not the same thing as a workload instance’s state. Earlier approaches often buried group semantics in a scheduler extension, annotations, or controller-specific conventions. They can work. But the application controller, scheduler, observability stack, and API status often end up with slightly different definitions of what “started” means.
v1.37 separates those concerns. A Workload is the durable policy object. A PodGroup is a runtime instance. Pods point to that instance through spec.schedulingGroup. It looks modest on paper, but it removes a surprising amount of hidden coupling in operation.
Consider a PyTorch DistributedDataParallel run with one coordinator and 16 GPU workers. Classic scheduling may bind the coordinator and several workers, then leave the rest pending because another batch job consumed a few GPUs. The coordinator may initialize rendezvous, workers may retry, and the job can sit in a costly partial-start state. Gang scheduling changes the question from “can I schedule this Pod?” to “can I satisfy minCount for this group now?” If the answer is no, none of the gang binds.
A pending whole job is visible capacity pressure. A half-started distributed job is usually wasted capacity wearing a successful-looking mask.
The Beta APIs live in scheduling.k8s.io/v1beta1 behind the GenericWorkload feature gate. They are disabled by default. CompositePodGroup is in scheduling.k8s.io/v1alpha3, behind the separately disabled CompositePodGroup gate. The Job-controller integration requires WorkloadWithJob. This is deliberate opt-in infrastructure, not a switch application teams should discover through a random Helm value.
Model scheduling policy with Workload and PodGroup APIs
Workload is the contract; PodGroup is the instance
A Workload defines one or more PodGroupTemplates. Each template has a unique name, a schedulingPolicy of basic or gang, plus optional priority and disruption parameters. Its specification is immutable. Don’t fight that constraint; use it. A scheduling policy should behave more like an SLA contract than a mutable runtime knob.
One Workload can contain up to eight PodGroupTemplates. That covers a practical multi-phase definition: data preparation, trainer workers, parameter services, checkpoint writers, evaluation, and cleanup. It does not invite you to encode an entire workflow engine in one object. I’d rather have two comprehensible Workloads than one heroic object nobody can safely change.
The controller creates a PodGroup for each execution and references the selected template through spec.podGroupTemplateRef. The PodGroup receives the runtime policy and status. The scheduler reports PodGroupInitiallyScheduled as True only when the group’s initial placement meets the relevant requirement. For a gang, that means the required count can be placed. For a basic group, Pods remain independently schedulable while still gaining shared identity and status.
# Conceptual runtime ordering; create policy and group before Pods.
Workload
└── PodGroupTemplate: trainer-gang, schedulingPolicy: gang
└── PodGroup: run-2026-10-03-trainers
└── Pod.spec.schedulingGroup.podGroupName: run-2026-10-03-trainers
That ordering matters. A Pod that references a nonexistent PodGroup remains pending. So does a PodGroup whose parent CompositePodGroup is missing. Create and observe the parent objects first, then create Pods. A GitOps pipeline can apply manifests in dependency order, but a custom controller should also use owner references, idempotent reconciliation, explicit readiness checks, and status observation instead of assuming API submission equals scheduler visibility.
minCount is a capacity commitment, not a wish
For gang, minCount defines how many Pods must be placeable before any member binds. Setting it to the complete worker count is the cleanest model for synchronous training. Setting it below the replica count can work for frameworks that tolerate elastic membership, but the application’s rendezvous and checkpoint logic must actually support that behavior. A scheduler can’t repair a training framework that silently assumes 16 ranks after you admitted 12.
Priority, preemptionPolicy, disruptionMode, and minCount make a PodGroup a resource-governance object. A PriorityClass can distinguish a deadline-sensitive simulation from opportunistic feature generation. Preemption can help a critical group acquire enough capacity. It can also turn low-priority queues into permanent waiting rooms when teams assign elevated priority casually. This is the wrong default: make high-priority classes scarce, reviewed, and observable.
Expose four measurements from the start: time from PodGroup creation to PodGroupInitiallyScheduled=True, pending group count by priority, preemptions attributed to a PodGroup, and GPU allocation failures by requested capability. Grafana dashboards should show the group, not just aggregate Pending Pods. Prometheus alerts keyed only to individual Pods make a gang-scheduling incident look like 16 unrelated inconveniences.

PodGroups wait for enough capacity before coordinated workloads begin.
CompositePodGroup and hierarchical placement
CompositePodGroup is where v1.37 gets more ambitious. It expresses a hierarchy rather than a flat collection: multiple data-loader groups, several trainer groups, evaluator groups, and checkpoint-writer groups can belong to a parent scheduling tree. The Kubernetes documentation describes Workloads defining spec.compositePodGroupTemplates, then controllers instantiating CompositePodGroups and PodGroups at runtime.
Picture a tree from top to bottom. At the root sits a CompositePodGroup representing one training run and its placement boundary. Beneath it sit trainer groups, data-loader groups, evaluator groups, and checkpoint-writer groups. At the leaves are individual Pods, each with CPU, memory, storage, network topology, GPU, and placement requirements. The scheduler needs the tree in place before it can make a coherent decision. Missing parents stop scheduling rather than allowing accidental partial placement.
This is a credible pattern for placement-sensitive jobs. A platform can represent trainer and evaluation groups together while retaining separate disruption and priority rules. It also fits complex batch designs where upstream readers and downstream workers must be admitted as a coordinated unit. The Alpha label matters, though. Build a feature-flagged controller path, preserve a flat PodGroup fallback, and rehearse disablement. Running Alpha scheduling semantics without a rollback plan is bravado, not engineering.
Topology constraints still need careful design. A hierarchy does not erase node labels, affinity, taints, tolerations, availability zones, or capacity fragmentation. A group requiring GPUs in a particular zone can remain pending even when the cluster reports spare GPU capacity elsewhere. That is useful information. Your capacity dashboard should report allocatable devices by topology domain, not merely one seductive cluster-wide total.
Pair gang scheduling with DRA GPUs on AWS EKS
Gang scheduling decides whether a group can start coherently. Dynamic Resource Allocation decides how specialized hardware is selected and allocated. You want both. Kubernetes v1.37 brings DRA Extended Resource support to General Availability, while the DRA updates blog identifies Partitionable Devices and Consumable Capacity as Beta capabilities. DRA Device Taints and Tolerations are stable, allowing drivers and administrators to require an explicit tolerance before a Pod can use a tainted device.
Amazon EKS supports Kubernetes 1.37 and enables DRA core APIs by default, alongside Beta support for Partitionable Devices and Consumable Capacity. For supported node models, install a DRA driver such as the NVIDIA DRA driver when using Karpenter static capacity, managed node groups, or self-managed nodes. EKS Auto Mode does not support the DRA driver; use the NVIDIA device plugin there instead. Make that platform decision early, before application teams depend on DRA-specific behavior.
Here is the practical sequence for a GPU training run. The controller materializes the Workload-derived PodGroup. Each worker Pod requests its GPU resources through the installed DRA driver. DRA evaluates device availability and, where configured, Device Binding Conditions establish that the selected device is ready. The scheduler evaluates the group’s minCount against node, topology, device, and policy constraints. Only when the requirements can be satisfied does the gang bind and the PodGroup become initially scheduled.
This closes a dangerous gap common in GPU platforms: a scheduler sees nominal accelerator capacity, but the application starts against devices that are unavailable, inappropriate, not ready, or mismatched. DRA cannot make hardware failure disappear. It does provide a standardized allocation interface and a place to enforce readiness before the group is treated as scheduled.
Don’t confuse DRA with an automatic utilization cure. Partitionable Devices and Consumable Capacity can improve sharing efficiency, but a gang with rigid topology and a large minCount can still fragment a cluster. Run load tests with the exact GPU shapes, availability-zone constraints, priorities, and concurrent-job mix you expect. A synthetic test that submits one pristine training job into an empty cluster proves almost nothing. It’s fine for a prototype, not for a fleet.
Operating Kubernetes v1.37 Workload-aware scheduling safely
Feature-gate activation is a control-plane change. Enable GenericWorkload for the Workload and PodGroup APIs, WorkloadWithJob if built-in Job integration is required, CompositePodGroup only after accepting its Alpha operational risk, and the relevant controller configuration. The Kubernetes v1.37 documentation calls for configuration across kube-apiserver, kube-scheduler, and relevant controllers. In EKS, verify the supported cluster configuration and bootstrap override path for the chosen release rather than assuming you can edit managed control-plane static Pod manifests.
The Job controller can compile a Job’s .spec.scheduling configuration into Workload and PodGroup objects before it creates Pods when WorkloadWithJob is enabled. That is a sensible entry point for teams with existing batch conventions. It avoids a custom controller on day one while still making group policy explicit. Custom CRD controllers remain appropriate when a framework has lifecycle semantics beyond Job behavior, such as iterative trial management or coordinated checkpoint recovery.
Keep scheduling policy in a platform-owned, versioned repository. Publish reviewed templates for distributed PyTorch, Spark-like batch processing, MPI-style simulations, and long-running multi-Pod services with strict disruption requirements. Application repositories should provide workload parameters, not hand-author their own priority and preemption rules. We already centralize network and storage policy for good reasons; scheduling deserves the same discipline.
Use admission checks to reject obvious deadlocks before they become expensive mysteries. Validate that a referenced PodGroup exists, that group names are namespace-local, that requested replicas are compatible with minCount, and that requested DRA devices have a matching driver path on the target node pools. Then test the unhappy paths: delete a CompositePodGroup parent, exhaust one zone’s GPUs, taint devices without matching tolerations, and submit competing high-priority gangs. If the dashboards don’t tell a clear story, the design is incomplete.
The scheduling question that matters
Kubernetes v1.37 moves the scheduler closer to a programmable resource-orchestration layer. Workload and PodGroup APIs make group intent explicit. CompositePodGroup provides an experimental hierarchy for placement trees. DRA brings device allocation and readiness into the same operational conversation. EKS provides the managed substrate, with a real constraint around EKS Auto Mode and DRA drivers that should not be waved away.

Workload-aware scheduling points toward more deliberate, observable resource orchestration.
The concrete takeaway is simple: model the smallest group that must succeed together, set minCount to that truth, allocate GPUs through DRA where your EKS node model supports it, and measure PodGroup scheduling latency as a first-class SLO. Ask the hard question before the next expensive run: if this job starts with only half its members, is it actually running—or merely occupying hardware?