Instance Preferences

Define instance type preferences on NodePools using annotations to influence Karpenter provisioning.

Instance preferences allow you to express instance type preferences directly on Karpenter NodePools using annotations. Veneer watches NodePools and generates NodeOverlay resources for each preference, influencing Karpenter’s provisioning decisions.

NodeOverlays are preferences, not rules. When Veneer creates a NodeOverlay with a price adjustment, it influences but does not guarantee instance selection. See Instance Selection Deep Dive for how AWS makes the final decision.

Annotation Format

veneer.io/preference.N: "<matcher> [<matcher>...] adjust=[+-]N%"

Where:

  • N is a positive integer (1-9 recommended) that determines overlay weight/priority
  • <matcher> is key=value1,value2 or key!=value or key>value or key<value
  • adjust specifies the price adjustment percentage

Example NodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: my-workload
  annotations:
    # Prefer c7a/c7g families with 20% discount
    veneer.io/preference.1: "karpenter.k8s.aws/instance-family=c7a,c7g adjust=-20%"
    # Prefer ARM64 architecture with 30% discount
    veneer.io/preference.2: "kubernetes.io/arch=arm64 adjust=-30%"
    # Combined matcher: m7g on ARM64 with 40% discount
    veneer.io/preference.3: "karpenter.k8s.aws/instance-family=m7g kubernetes.io/arch=arm64 adjust=-40%"

Generated NodeOverlay

For preference veneer.io/preference.1 on the NodePool above, Veneer generates:

apiVersion: karpenter.sh/v1alpha1
kind: NodeOverlay
metadata:
  name: pref-my-workload-1
  labels:
    app.kubernetes.io/managed-by: veneer
    veneer.io/type: preference
    veneer.io/source-nodepool: my-workload
spec:
  requirements:
    - key: karpenter.sh/nodepool
      operator: In
      values: ["my-workload"]
    - key: karpenter.k8s.aws/instance-family
      operator: In
      values: ["c7a", "c7g"]
  priceAdjustment: "-20%"
  weight: 1

Supported Labels

The following Karpenter and Kubernetes labels can be used in matchers:

LabelDescriptionExample Values
karpenter.k8s.aws/instance-familyInstance familyc7a, m7g, r6i
karpenter.k8s.aws/instance-categoryInstance categoryc, m, r, t
karpenter.k8s.aws/instance-generationInstance generation number6, 7, 8
karpenter.k8s.aws/instance-sizeInstance sizelarge, xlarge, 2xlarge
karpenter.k8s.aws/instance-cpuNumber of vCPUs4, 8, 16
karpenter.k8s.aws/instance-cpu-manufacturerCPU manufacturerintel, amd, aws
karpenter.k8s.aws/instance-memoryMemory in MiB8192, 16384
kubernetes.io/archArchitectureamd64, arm64
karpenter.sh/capacity-typeCapacity typeon-demand, spot
node.kubernetes.io/instance-typeSpecific instance typem5.xlarge, c7g.2xlarge

Operators

SyntaxKubernetes OperatorDescriptionExample
=InMatch any of the valuesinstance-family=c7a,c7g
!=NotInExclude all of the valuesinstance-family!=t3,t3a
>GtGreater than (numeric)instance-cpu>4
<LtLess than (numeric)instance-cpu<64

Multiple Matchers

You can combine multiple matchers in a single preference to create compound requirements. All matchers must match for the overlay to apply:

# Prefer m7g instances on ARM64 with >= 8 vCPUs
veneer.io/preference.1: "karpenter.k8s.aws/instance-family=m7g kubernetes.io/arch=arm64 karpenter.k8s.aws/instance-cpu>7 adjust=-40%"

This generates a NodeOverlay with three requirements (plus the NodePool selector), all of which must be satisfied for the price adjustment to apply.

Weight and Priority

The number N in veneer.io/preference.N becomes the overlay weight:

  • Lower numbers = lower weight (lower priority)
  • Higher numbers = higher weight (higher priority)

Weight Hierarchy

Overlay TypeDefault WeightPriority
Reserved Instance overlays30Highest (most specific)
EC2 Instance SP overlays20Medium
Compute SP overlays10Lower
Preference overlaysN (1-9)Lowest

Keep preference numbers below 10 to ensure RI/SP overlays (backed by real AWS capacity data) take precedence over user-defined preferences.

Preference Lifecycle

EventAction
Preference annotation added to NodePoolVeneer creates a NodeOverlay
Preference annotation value changedVeneer updates the NodeOverlay
Preference annotation removedVeneer deletes the NodeOverlay
NodePool deletedNodeOverlay is garbage collected via owner reference

Common Patterns

Prefer ARM64 (Graviton)

annotations:
  veneer.io/preference.1: "kubernetes.io/arch=arm64 adjust=-30%"

Prefer Specific Instance Families

annotations:
  veneer.io/preference.1: "karpenter.k8s.aws/instance-family=c7g,m7g adjust=-25%"

Prefer Latest Generation

annotations:
  veneer.io/preference.1: "karpenter.k8s.aws/instance-generation>6 adjust=-15%"

Avoid Small Instances

annotations:
  veneer.io/preference.1: "karpenter.k8s.aws/instance-cpu>7 adjust=-10%"

Layered Preferences

You can stack preferences with increasing specificity and discounts:

annotations:
  # Slight preference for ARM64
  veneer.io/preference.1: "kubernetes.io/arch=arm64 adjust=-10%"
  # Stronger preference for Graviton 7th gen
  veneer.io/preference.2: "karpenter.k8s.aws/instance-generation=7 kubernetes.io/arch=arm64 adjust=-20%"
  # Strongest preference for c7g family specifically
  veneer.io/preference.3: "karpenter.k8s.aws/instance-family=c7g adjust=-30%"

Disabling Preferences

Preference processing can be disabled globally via configuration:

# config.yaml
preferences:
  enabled: false

When disabled, the NodePool reconciler will not generate overlays from veneer.io/preference.N annotations. Existing preference overlays will be cleaned up.