Skip to main content
Using a coding agent? Load the together-ai skill to teach your agent to write correct Kueue and GPU cluster code for Together AI. Learn more.
Running third-party schedulers on Together GPU clusters is in beta. The steps and pinned version here (Kueue v0.18.3) are validated on current clusters, but the workflow and defaults may change. Report issues through the support form.
Kueue is a Kubernetes-native job queueing controller. Instead of letting every job compete for GPUs the moment it is created, Kueue holds jobs in a queue and admits them only when their quota is available, suspending the rest. This is how you share a fixed GPU pool across teams without overcommitting it. This guide installs Kueue on a Together GPU cluster and gates jobs on GPU quota. For the concepts behind each object below, see the Kueue documentation.

Requirements

  • A Together Kubernetes GPU cluster in the Ready state.
  • Cluster-admin access, which the cluster’s admin kubeconfig grants. Only project admins can download the admin kubeconfig.
  • kubectl configured against the cluster. Download the admin kubeconfig with tg clusters get-credentials <CLUSTER_ID> --set-default-context --no-json.

Install Kueue

Apply the versioned release manifest. The bundled custom resource definitions are large, so pass --server-side:
Wait for the controller to become available:
The controller pod should be Running:
To install with Helm instead, install the chart from the registry: helm install kueue oci://registry.k8s.io/kueue/charts/kueue --version 0.18.3 --namespace kueue-system --create-namespace. For other install methods and configuration options, see the Kueue installation guide.

Define quotas

Kueue admits jobs against a quota that you define with three resources:
  • ResourceFlavor: Represents a class of nodes. One flavor is enough when your GPU nodes are homogeneous.
  • ClusterQueue: Defines the cluster-wide quota pool. It must cover every resource your jobs request, including CPU and memory. Kueue never admits a job that requests a resource the ClusterQueue doesn’t cover.
  • LocalQueue: Accepts the jobs users submit in its namespace and points to the ClusterQueue whose quota they draw on.
Create all three, with an eight-GPU quota on the ClusterQueue:
kueue-quota.yaml
Each resource’s nominalQuota sets how much of it the ClusterQueue can admit at once. The jobs in the following sections assume this eight-GPU quota. For your own workloads, set the GPU quota to the number of GPUs you want this queue to control.

Attach shared storage

To let queued jobs read and write the same dataset and checkpoints, bind a PersistentVolumeClaim to your cluster’s shared volume. Together provisions a static PersistentVolume named after the shared volume, so set volumeName to your shared volume’s name:
shared-pvc.yaml
Storage isn’t a quota resource, so the claim doesn’t affect admission. The next section mounts it into the job.

Submit a job to a queue

Point a standard batch/v1 Job at the LocalQueue with the kueue.x-k8s.io/queue-name label, and set suspend: true. Kueue takes over the suspend flag and sets it to false when it admits the job:
gpu-job.yaml
Kueue creates a Workload object for the job. Six GPUs fit within the eight-GPU quota, so Kueue admits the job and its pod starts. Confirm that the Workload is admitted:
For other supported workload types, such as JobSet, RayJob, and MPIJob, see running jobs with Kueue.

Queue a second job until quota frees up

Submit a second job that also requests six GPUs. The two jobs need 12 GPUs combined, which exceeds the eight-GPU quota. To create it, copy gpu-job.yaml, change metadata.name in the copy to gpu-job-b, and apply the copy. Compare the two jobs:
The first job runs, and the second stays suspended. The second job’s Workload reports why in its QuotaReserved condition:
When the first job finishes or you delete it, Kueue admits the queued job and its pod starts. You don’t need to resubmit it. Delete the first job, then check the second job’s suspend flag:

Troubleshooting

  • A job stays suspended and has no Workload: The kueue.x-k8s.io/queue-name label is missing, so Kueue ignores the job and never unsuspends it.
  • A job’s Workload says the LocalQueue doesn’t exist: The kueue.x-k8s.io/queue-name label names a LocalQueue that isn’t in the job’s namespace.
  • A job’s Workload is never admitted: Check the Workload’s QuotaReserved condition message. Either the job’s combined request exceeds the ClusterQueue quota, or the ClusterQueue doesn’t cover every resource the job requests. A ClusterQueue that covers only GPUs never admits a job whose pods also request CPU and memory, so add them to coveredResources.
  • The ClusterQueue shows no active quota: Its ResourceFlavor is missing or misnamed. Each flavors[].name must match the name of a ResourceFlavor.

Next steps

Gang-schedule GPU jobs with Volcano

Run distributed training with all-or-nothing gang scheduling.

Run workloads on Kubernetes

Deploy pods with persistent storage and monitor them with Grafana.