AIM Profiles#
An AIM profile is a self-contained runtime configuration for an inference workload. It answers five questions about a deployment without consulting any other resource:
Model architecture — What model family does this serve? (
aimId)Accelerator — What hardware is required? (
acceleratorType,acceleratorModel,acceleratorCount)Engine — How is the inference engine configured? (
engineArgs,engineEnv)Container image — What image runs the workload? (
image)Optimization target — What was this profile tuned for? (
metric,precision,type)
AIMProfile and AIMClusterProfile carry this configuration. An AIMService resolves to exactly one of them and deploys it.
v1alpha2
Profiles are part of aim.eai.amd.com/v1alpha2. They replace v1alpha1 Service Templates, which are deprecated.
Where profiles come from#
Most profiles aren’t hand-authored. They’re produced by AIMModel reconcilers in one of three flows:
Source |
Profile origin |
Typical labels |
|---|---|---|
Image discovery on an official AIM image |
|
|
Image discovery on a base AIM image |
|
|
|
|
|
Hand-authored by a user |
|
(none of the |
See AIM Models for the three model flows that produce these profiles.
Cluster vs namespace scope#
Resource |
Scope |
Caching support |
|---|---|---|
|
Cluster |
No |
|
Namespace |
|
When both a namespace-scoped and cluster-scoped profile match a service’s selector, the namespace-scoped profile takes precedence.
Deployable vs base#
A profile is deployable when it carries both spec.aimId and a non-empty spec.modelSources. The model controller stamps this onto status.deployable: true and labels the profile aim.eai.amd.com/profile-role=deployable.
A profile is a base profile when both aimId and modelSources are empty. Base profiles carry status.deployable: false and profile-role=base. They exist only as derivation sources for custom-model AIMModels — they cannot back an AIMService directly.
Mixed spec (one of aimId / modelSources set, the other empty) is rejected at admission so status.deployable is always derivable from spec.
Provenance labels#
v1alpha2 stamps a small set of canonical labels on every operator-produced profile. These are the labels selectors key off, both inside the AIMService resolver and inside AIMProfileSet derivation.
Label |
Values |
|---|---|
|
|
|
|
|
Name of the owning |
|
|
|
|
|
|
The matching status fields mirror the labels for kubectl-friendly access:
status.originmirrorsprofile-originstatus.sourceModel.{name,kind,namespace}mirrorssource-model+source-model-scopestatus.deployablederives from spec (also implicit fromprofile-role)
See Naming and Labels → Profile labels for the canonical list (including label setters and selector recipes).
Filtering profiles by aimId (field selector)#
AIMProfile and AIMClusterProfile expose spec.aimId as a selectable field, so clients can filter profiles by model architecture server-side with a field selector instead of listing everything and filtering client-side:
# Cluster profiles for one model architecture
kubectl get aimclusterprofile --field-selector spec.aimId=qwen/qwen3-32b
# Namespace profiles for one model architecture
kubectl get aimprofile -n ml-team --field-selector spec.aimId=qwen/qwen3-32b
This is the recommended way for the management UI (and any API consumer) to scope a profile listing to a single model — it pushes the filter to the API server, avoiding a full list + client-side scan. Selectable fields on custom resources require Kubernetes 1.32+ (the CustomResourceFieldSelectors feature), which matches the project’s minimum supported version (see Prerequisites).
Only spec.aimId is wired as a selectable field today. Other axes (spec.engine, spec.precision, spec.acceleratorModel, status.deployable, …) are natural candidates and can be added the same way (Kubernetes allows up to 8 selectable fields per CRD version).
Profile specification#
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMClusterProfile
metadata:
name: qwen-qwen3-32b-mi300x-lat-fp8
spec:
aimId: qwen/qwen3-32b
modelId: qwen/qwen3-32b-fp8
engine: vllm
metric: latency
precision: fp8
type: optimized
primary: true
acceleratorModel: MI300X
acceleratorType: gpu
acceleratorCount: 1
resources:
requests:
cpu: "4"
memory: 32Gi
image: amdenterpriseai/aim-qwen-qwen3-32b:0.8.5
engineArgs:
distributed_executor_backend: mp
gpu-memory-utilization: "0.95"
engineEnv:
VLLM_DO_NOT_TRACK: "1"
modelSources:
- modelId: qwen/qwen3-32b-fp8
sourceUri: hf://qwen/qwen3-32b-fp8
Spec fields#
Field |
Description |
|---|---|
|
Model architecture identifier (e.g. |
|
Specific model variant or weights identifier (e.g. |
|
On-disk profile identifier from the AIM image (e.g. |
|
Inference engine ( |
|
Optimization target: |
|
Numeric precision: |
|
Optimization level. Hierarchy: |
|
Marks the recommended default for this model + hardware combination. Boosts ranking during automatic selection. Default |
|
Deprecated and ignored by the resolver. Formerly excluded a profile from automatic selection; that role is now served by |
|
Accelerator identifier for node selection (e.g. |
|
|
|
Number of accelerator units required. Combined with |
|
Optional override for K8s |
|
Required. Deployment container image. For purpose-built profiles: the full AIM image. For overlay-produced custom-model profiles: the base image. |
|
Inference engine CLI arguments as a free-form JSON object. Typed values (ints, floats, booleans, strings) preserved; converted to |
|
Environment variables passed to the inference engine subprocess (distinct from |
|
Model artifact sources with download URIs. |
|
Container-level env vars on the pod spec. |
|
Secrets for pulling the deployment image. |
|
Workload service account. |
Namespace-scoped AIMProfile adds one more field:
Field |
Description |
|---|---|
|
When |
Accelerator and node affinity#
The three accelerator fields jointly describe the hardware AIM Engine schedules onto.
spec.acceleratorModel: MI300X
→ feature.node.kubernetes.io/aim-accelerator.MI300X (Exists)
spec.acceleratorType: gpu
spec.acceleratorCount: 1
→ AIM Engine computes the default device request (e.g. amd.com/gpu: "1")
The AcceleratorDetector DaemonSet labels each node with all applicable identifiers (specific GPU model, CPU architecture). A profile with acceleratorModel: MI300X matches exactly that GPU model; a fallback profile with acceleratorModel: EPYC_ZEN5 matches any Zen5 EPYC node.
The label value (count) is informational only — the selector operator is Exists. Actual capacity is enforced via the computed device resource request.
Hardware support#
Different AIM images support different AMD hardware families:
Family |
Example |
|---|---|
AMD Instinct |
|
AMD Radeon |
|
AMD EPYC |
|
The hardware a profile targets is declared on the profile itself, via AIMProfile.spec.acceleratorModel:
# AIMProfile (or AIMClusterProfile)
spec:
acceleratorModel: MI300X
You don’t normally author these profiles by hand — the AIMModel discovers the AIM image and publishes one profile per supported (hardware, precision, metric) combination, each stamped with the spec.acceleratorModel it was built for. See Where profiles come from. To target specific hardware you therefore pick among the already-discovered profiles rather than inventing a new accelerator value.
An AIMService selects a profile with that value through spec.profile.selector.acceleratorModel — the selector matches against the profiles’ spec.acceleratorModel and resolves the service to a profile carrying it:
# AIMService
spec:
model:
name: qwen-qwen3-32b
profile:
selector:
acceleratorModel: MI300X
Make sure the chosen model image actually supports the target hardware — an image built for AMD Instinct GPUs will not run on Radeon or EPYC. Profile resolution only matches an image to a node whose detected labels satisfy the profile’s spec.acceleratorModel; it does not transcode an unsupported image onto incompatible hardware. See Deploying Services — Model + selector for the full selector resolution flow.
Partitioned GPUs#
For partitioned GPU configurations (CPX-NPS4, MIG, etc.), override the derived device resource in spec.resources:
spec:
acceleratorModel: MI300X
acceleratorType: gpu
acceleratorCount: 0
resources:
requests:
amd.com/cpx-nps4: "4"
acceleratorCount: 0 suppresses the default amd.com/gpu derivation so the override stands alone.
CPU-only profiles#
Profiles without accelerator fields are treated as CPU-only and are always Ready (no node-affinity gating).
spec:
aimId: microsoft/phi-2
image: amdenterpriseai/aim-phi-2:0.8.5
engine: vllm
metric: latency
precision: fp32
Profile status#
Field |
Description |
|---|---|
|
|
|
|
|
|
|
Base image reference extracted from the discovery-cache metadata ( |
|
|
|
Extracted from |
|
Count of cluster nodes matching the accelerator label and resource requests |
|
Human-readable summary ( |
|
Definitive |
|
Computed node affinity rules |
|
Standard Kubernetes conditions |
Status lifecycle#
Pending — profile created, not yet reconciled
Ready — at least one cluster node matches the accelerator label and has sufficient resource capacity. CPU-only profiles are always
Ready.Degraded — transient error (e.g. failed to list nodes); will be retried.
NotAvailable — no cluster nodes match the requirements. Auto-recovers when matching nodes are added.
Conditions#
HardwareAvailable reports whether the cluster has nodes matching the profile’s requirements:
Status |
Reason |
Description |
|---|---|---|
|
|
Matching nodes present |
|
|
No accelerator requirements — always available |
|
|
No matching nodes |
The profile controller watches node events and re-evaluates hardware availability whenever node labels change.
Primary profiles#
spec.primary: true marks a profile as the recommended default for its (model + accelerator + precision + metric) combination. AIM image authors stamp this on the profile they consider the production sweet spot for the hardware.
When a service uses spec.model.name resolution (which produces multiple candidate profiles), primaries are ranked above non-primaries during selection. Non-primary profiles remain addressable by explicit name.
Optimization tier and the minimumType floor#
A profile’s spec.type (optimized > general > preview > unoptimized) is both a ranking signal and a selection gate. Automatic selection (spec.model.name / selector resolution) only considers profiles whose type is at or above the selector’s spec.profile.selector.minimumType floor. For AIMServices that floor defaults to optimized, so preview/unoptimized profiles (e.g. CPU/EPYC tunings published as unoptimized) are never auto-picked unless the service opts in:
# AIMService — accept lower tiers (e.g. an EPYC unoptimized profile)
spec:
model:
name: aim-epyc-llama-3-2-1b-zen4-lowmem
profile:
selector:
minimumType: any # or "unoptimized"; default would be "optimized"
Any profile — including lower tiers — also remains addressable by explicit spec.profile.name, which bypasses the floor entirely.
!!! warning “manualSelectionOnly is deprecated”
spec.manualSelectionOnly is no longer honored. It was a binary “exclude from auto-selection” gate, redundant with the graded type hierarchy. Express the same intent by publishing the profile as type: unoptimized (the resolver’s default minimumType: optimized floor then keeps it opt-in). The field is still accepted for backward compatibility but has no effect and will be removed in a future API version.
Examples#
Cluster profile — latency-tuned#
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMClusterProfile
metadata:
name: qwen-qwen3-32b-mi300x-lat-fp8
spec:
aimId: qwen/qwen3-32b
modelId: qwen/qwen3-32b-fp8
engine: vllm
metric: latency
precision: fp8
type: optimized
primary: true
acceleratorModel: MI300X
acceleratorType: gpu
acceleratorCount: 1
image: amdenterpriseai/aim-qwen-qwen3-32b:0.8.5
engineArgs:
gpu-memory-utilization: "0.95"
Namespace profile — custom weights with caching#
apiVersion: aim.eai.amd.com/v1alpha2
kind: AIMProfile
metadata:
name: my-finetuned-qwen-mi300x-lat-fp8
namespace: ml-team
spec:
aimId: qwen/qwen3-32b
modelId: my-org/qwen-finetuned-fp8
engine: vllm
metric: latency
precision: fp8
type: general
acceleratorModel: MI300X
acceleratorType: gpu
acceleratorCount: 2
resources:
requests:
cpu: "8"
memory: 64Gi
image: amdenterpriseai/aim-base:0.8.5
engineArgs:
tensor-parallel-size: "2"
modelSources:
- modelId: my-org/qwen-finetuned-fp8
sourceUri: s3://my-bucket/fp8-weights/
caching:
enabled: true
Troubleshooting#
Profile stuck in NotAvailable#
The profile’s accelerator requirements don’t match any cluster node.
kubectl get aimclusterprofile <name> -o jsonpath='{.status.resolvedNodeAffinity}' | jq
kubectl get aimclusterprofile <name> -o jsonpath='{.status.resources}' | jq
kubectl get aimclusterprofile <name> -o jsonpath='{.status.matchingNodes}'
# Nodes carrying the expected accelerator label
kubectl get nodes -l feature.node.kubernetes.io/aim-accelerator.MI300X
Common causes: GPU nodes not yet added, AcceleratorDetector not labeling nodes, wrong acceleratorModel value.
source-model label disappears after reconcile#
Expected for user-authored profiles. See Hand-authored profiles cannot fake provenance above.
Profile is Ready but a service can’t resolve to it#
If the service uses spec.profile.selector or spec.model.name, the selector may not match this profile. Check:
The profile’s
status.deployableistrue(base profiles are never returned).The profile carries
profile-role: deployable.For
modelRef.nameselectors, the profile carriessource-model: <model-name>. User-authored profiles don’t carry this label — they’re reachable only byaimId/modelId.