Getting Started with AIPerf on Kubernetes
AI Agents (Claude, Copilot, Cursor, etc.): For diagnosing failures, see the AI Agent Debugging Guide.
This guide walks you through benchmarking an NVIDIA Dynamo inference server on Kubernetes using AIPerf. By the end, you will have a cluster running, both operators installed, a benchmark executed against a Dynamo deployment, and your results downloaded.
The same workflow applies to any OpenAI-compatible endpoint (vLLM, TRT-LLM, SGLang) — just change the endpoint URL and model name.
Cluster Setup
If you already have a Kubernetes cluster with GPU nodes and kubectl configured, skip to Prerequisites.
Option A: Use an Existing Cluster
Any Kubernetes v1.24+ cluster with NVIDIA GPUs works. You need:
kubectlconfigured to talk to the cluster- GPU nodes with the NVIDIA device plugin installed
- Permissions to install CRDs and create namespaces
Verify your connection:
If GPUs show up, skip to Prerequisites.
Option B: Create a Local Kind Cluster with GPU Passthrough
Kind runs Kubernetes inside Docker containers on your local machine. With the NVIDIA container runtime configured as Docker’s default, a Kind node can see the host’s GPUs.
Host requirements:
- Docker with the NVIDIA container runtime as the default runtime
- NVIDIA drivers installed on the host
kindCLI installed (go install sigs.k8s.io/kind@latestor releases)
One-time Docker setup
If you haven’t already configured Docker for GPU passthrough:
Verify:
Cluster setup
Create the cluster:
Install the NVIDIA device plugin so GPUs become an allocatable resource, and JobSet, which the AIPerf operator uses to run benchmark pods:
Build the AIPerf image locally and load it into the Kind node so pods can pull it without a registry:
Verify GPUs are allocatable:
Teardown
To delete the cluster when you are done:
Prerequisites
At this point you should have:
- A Kubernetes cluster with
kubectlconfigured - GPU nodes with the NVIDIA device plugin
- JobSet installed
- Helm v3 installed locally
- AIPerf installed locally (
uv tool install aiperf, oruv syncfrom a source checkout) - Access to NGC container registry (
nvcr.io/nvidia/ai-dynamo)
Run the preflight checker to verify:
Step 1: Install the Operators
You need two operators: the Dynamo operator (manages inference servers) and the AIPerf operator (manages benchmarks).
Install the Dynamo Operator
Verify the Dynamo operator is running:
Install the AIPerf Operator
For Kind clusters using a locally built image, override the image:
Verify it is running:
You should see 2/2 containers ready by default (operator + results-server). With the optional Plotly dashboard enabled (dashboard.enabled=true), the count becomes 3/3 (operator + results-server + dashboard).
Step 2: Deploy a Dynamo Inference Server
Create a DynamoGraphDeployment. This example deploys Qwen3-0.6B in aggregated mode using the vLLM backend:
Apply it:
Wait for the server to be ready (model loading can take a few minutes):
Verify the endpoint is healthy:
Step 3: Run Your First Benchmark
Now benchmark the Dynamo server. The Dynamo endpoint URL follows the pattern http://{deployment-name}-frontend.{namespace}.svc:8000/v1:
On a Kind cluster with a locally built image, point --image at the loaded tag and disable pulling. Substitute the Service URL of whatever OpenAI-compatible endpoint you are testing against:
What happens:
- AIPerf builds an
AIPerfJobcustom resource from your flags - Submits it to the cluster, where the AIPerf operator picks it up
- The operator creates RBAC, a ConfigMap, and a JobSet with a controller pod and worker pods
- Workers send requests to the Dynamo frontend
- AIPerf polls the
AIPerfJobstatus and streams progress to your terminal
You will see the job phase, worker readiness (ready/total), and each status condition as the operator reports it. In --no-operator mode AIPerf attaches to the controller pod and tails its output instead.
Press Ctrl+C to detach. The benchmark continues running in the cluster. To cancel it, run aiperf kube cancel or patch the CR directly:
Step 4: Using a Config File
For repeatable benchmarks, use an AIPerfJob YAML file. Generate a starter template:
Edit it for your Dynamo deployment:
Validate the config before deploying:
Run it:
Or apply it directly with kubectl (the operator picks it up automatically):
Step 5: Disaggregated Inference
Dynamo’s disaggregated mode separates prefill and decode into different pods for better GPU utilization. To benchmark a disaggregated deployment:
- Deploy Dynamo in disaggregated mode (separate prefill and decode workers with KV cache transfer):
- Benchmark it — the endpoint URL changes to match the deployment name:
Step 6: View Results
After the benchmark completes, retrieve your results:
This downloads the full results package including:
profile_export_aiperf.json— Summary metrics (throughput, latency percentiles, TTFT, ITL)inputs.json— Dataset that was usedserver_metrics_export.json— Dynamo server metrics (frontend throughput, KV cache stats, component latencies); empty when discovery found no scrapable endpoints
To retrieve results from a specific job:
Results are stored on the operator’s persistent volume by default, so aiperf kube results works even after benchmark pods are deleted. To retrieve directly from the benchmark pods instead (downloads every artifact through the controller’s results API, so the controller pod must still be running):
Adding --summary-only narrows the download to the summary files and falls back to kubectl cp when the controller API is unreachable.
Dynamo Server Metrics
When benchmarking Dynamo, AIPerf discovers and collects Prometheus metrics from pods with the nvidia.com/metrics-enabled=true label (or a recognizable inference-server image). By default discovery only searches the benchmark job’s own namespace — the namespace the chart-provisioned benchmark RBAC can list pods in. If Dynamo runs in a different namespace (e.g. dynamo-server), set server_metrics.discovery.namespace: dynamo-server in the benchmark config and grant pod-read access there by adding that namespace to the chart’s serverMetricsDiscoveryNamespaces value — a plain string entry binds the benchmark namespaces’ default ServiceAccount, so benchmark pods running under a custom podTemplate.serviceAccountName need the {namespace, serviceAccounts} entry form (or a manual RoleBinding); see the server-metrics guide for the full RBAC prerequisite table and how to tune or disable discovery. Discovered metrics include:
- Frontend metrics —
dynamo_frontend_requests,dynamo_frontend_time_to_first_token_seconds,dynamo_frontend_inter_token_latency_seconds,dynamo_frontend_output_tokens - Component metrics — Per-worker
dynamo_component_requests,dynamo_component_kvstats_gpu_cache_usage_percent,dynamo_component_kvstats_gpu_prefix_cache_hit_rate
These are exported alongside the standard AIPerf metrics in the results package.
Step 7: List and Manage Jobs
See all benchmark jobs across namespaces:
WORKERS is ready/total and LATENCY is the p99 request latency. OWNER is
the scoped operator holding that namespace’s claim, - when the cluster-wide
operator reconciles it, or ? when the claim could not be read. Use --wide to add model, endpoint, and error columns.
Filter by status:
Watch jobs with live refresh:
Web Dashboard
The AIPerf operator includes a built-in web dashboard for monitoring benchmarks and analyzing results.
Access it by port-forwarding to the operator:
Then open http://localhost:8081 in your browser.
The dashboard provides:
- Dashboard — Overview with KPI cards, active jobs, and throughput trends
- Jobs — Sortable table of all benchmark jobs with phase filters
- Job Detail — Live metrics, charts, phase progress, and pod status for a single job
- Leaderboard — Rank benchmark runs by any metric
- Compare — Side-by-side comparison of multiple jobs
- History — Time-series charts showing metrics across runs
- Sweeps — AIPerfSweep listings and per-sweep variation drill-down
Use Ctrl+K to quickly search and navigate between jobs and pages.
Dynamo Deployment Modes
Backends
Dynamo supports three inference backends. Change the worker image and command:
Without the Operator
If you cannot install the AIPerf operator (e.g., limited cluster permissions), AIPerf can deploy benchmarks directly using raw Kubernetes manifests. Use --no-operator:
This creates RBAC, ConfigMap, and JobSet directly in an existing namespace. You lose operator features (automated monitoring, results storage, conditions) but the benchmark itself works the same way.
To generate the manifests without applying them (useful for GitOps):
Quick Reference
Next Steps
- Deploy from a Source Checkout — Build and push AIPerf, install the operator with Helm, and run on a real cluster
- Kubernetes Configuration Reference — All CRD fields, deployment options, and config patterns
- Monitoring and Troubleshooting — Watch, debug, and diagnose benchmark issues
- Production Deployments — CI/CD, Kueue scheduling, secrets, and GitOps workflows
Related Documentation
- YAML Config Reference — General AIPerf YAML configuration
- Sequence Length Distributions — ISL/OSL distribution configuration
- Architecture — AIPerf internal architecture