# NodeClasses

> Configure AWS-specific settings with EC2NodeClasses


Node Classes enable configuration of AWS specific settings.
Each NodePool must reference an EC2NodeClass using `spec.template.spec.nodeClassRef`.
Multiple NodePools may point to the same EC2NodeClass.

```yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  kubelet:
    podsPerCore: 2
    maxPods: 20
    systemReserved:
      cpu: 100m
      memory: 100Mi
      ephemeral-storage: 1Gi
    kubeReserved:
      cpu: 200m
      memory: 100Mi
      ephemeral-storage: 3Gi
    evictionHard:
      memory.available: 5%
      nodefs.available: 10%
      nodefs.inodesFree: 10%
    evictionSoft:
      memory.available: 500Mi
      nodefs.available: 15%
      nodefs.inodesFree: 15%
    evictionSoftGracePeriod:
      memory.available: 1m
      nodefs.available: 1m30s
      nodefs.inodesFree: 2m
    evictionMaxPodGracePeriod: 60
    imageGCHighThresholdPercent: 85
    imageGCLowThresholdPercent: 80
    cpuCFSQuota: true
    clusterDNS: ["10.0.1.100"]
  # Optional, dictates UserData generation and default block device mappings.
  # May be ommited when using an `alias` amiSelectorTerm, otherwise required.
  amiFamily: AL2

  # Required, discovers subnets to attach to instances
  # Each term in the array of subnetSelectorTerms is ORed together
  # Within a single term, all conditions are ANDed
  subnetSelectorTerms:
    # Select on any subnet that has the "karpenter.sh/discovery: ${CLUSTER_NAME}"
    # AND the "environment: test" tag OR any subnet with ID "subnet-09fa4a0a8f233a921"
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}"
        environment: test
    - id: subnet-09fa4a0a8f233a921

  # Required, discovers security groups to attach to instances
  # Each term in the array of securityGroupSelectorTerms is ORed together
  # Within a single term, all conditions are ANDed
  securityGroupSelectorTerms:
    # Select on any security group that has both the "karpenter.sh/discovery: ${CLUSTER_NAME}" tag
    # AND the "environment: test" tag OR any security group with the "my-security-group" name
    # OR any security group with ID "sg-063d7acfb4b06c82c"
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}"
        environment: test
    - name: my-security-group
    - id: sg-063d7acfb4b06c82c

  # Optional, IAM role to use for the node identity.
  # Must specify one of "role" or "instanceProfile" for Karpenter to launch nodes
  role: "KarpenterNodeRole-${CLUSTER_NAME}"

  # Optional, IAM instance profile to use for the node identity.
  # Must specify one of "role" or "instanceProfile" for Karpenter to launch nodes
  instanceProfile: "KarpenterNodeInstanceProfile-${CLUSTER_NAME}"

  # Each term in the array of amiSelectorTerms is ORed together
  # Within a single term, all conditions are ANDed
  amiSelectorTerms:
    # Select on any AMI that has both the `karpenter.sh/discovery: ${CLUSTER_NAME}`
    # AND `environment: test` tags OR any AMI with the name `my-ami` OR an AMI with
    # ID `ami-123` OR an AMI with ID matching the value of my-custom-parameter
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}"
        environment: test
    - name: my-ami
    - id: ami-123
    - ssmParameter: my-custom-parameter # ssm parameter name or ARN
    # Select EKS optimized AL2023 AMIs with version `v20240703`. This term is mutually
    # exclusive and can't be specified with other terms.
    # - alias: al2023@v20240703

  # Optional, each term in the array of capacityReservationSelectorTerms is ORed together.
  capacityReservationSelectorTerms:
    - tags:
        karpenter.sh/discovery: ${CLUSTER_NAME}
    - id: cr-123
    - instanceMatchCriteria: open

  # Optional, the terms are exclusive
  placementGroupSelector:
    name: my-pg
    id: pg-123

  # Optional, propagates tags to underlying EC2 resources
  tags:
    team: team-a
    app: team-a-app

  # Optional, configures IMDS for the instance
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 1 # This is changed to disable IMDS access from containers not on the host network
    httpTokens: required

  # Optional, configures ENI Connection Tracking timeouts.
  # Omit entirely to use EC2 defaults. Only set fields you want to override.
  connectionTracking:
    # Optional, configures timeout (in seconds) for idle TCP connections.
    tcpEstablishedTimeout: 300 # lower than the EC2 default of 432000 (5 days)
    # Optional, configures timeout (in seconds) for idle UDP stream flows.
    udpStreamTimeout: 120
    # Optional, configures timeout (in seconds) for idle unidirectional or single-transaction UDP flows.
    udpTimeout: 45

  # Optional, configures storage devices for the instance
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
        iops: 10000
        encrypted: true
        kmsKeyID: "1234abcd-12ab-34cd-56ef-1234567890ab"
        deleteOnTermination: true
        throughput: 125
        snapshotID: snap-0123456789
        volumeInitializationRate: 100

  # Optional, configures the network interfaces for the instance
  networkInterfaces:
    - networkCardIndex: 0
      deviceIndex: 0
      interfaceType: "interface"
    - networkCardIndex: 0
      deviceIndex: 1
      interfaceType: "interface"

  # Optional, use instance-store volumes for node ephemeral-storage
  instanceStorePolicy: RAID0

  # Optional, overrides autogenerated userdata with a merge semantic
  userData: |
    echo "Hello world"

  # Optional, configures detailed monitoring for the instance
  detailedMonitoring: true

  # Optional, configures if the instance should be launched with an associated public IP address.
  # If not specified, the default value depends on the subnet's public IP auto-assign setting.
  associatePublicIPAddress: true
status:
  # Resolved subnets
  subnets:
    - id: subnet-0a462d98193ff9fac
      zone: us-east-2b
    - id: subnet-0322dfafd76a609b6
      zone: us-east-2c
    - id: subnet-0727ef01daf4ac9fe
      zone: us-east-2b
    - id: subnet-00c99aeafe2a70304
      zone: us-east-2a
    - id: subnet-023b232fd5eb0028e
      zone: us-east-2c
    - id: subnet-03941e7ad6afeaa72
      zone: us-east-2a

  # Resolved security groups
  securityGroups:
    - id: sg-041513b454818610b
      name: ClusterSharedNodeSecurityGroup
    - id: sg-0286715698b894bca
      name: ControlPlaneSecurityGroup-1AQ073TSAAPW

  # Resolved AMIs
  amis:
    - id: ami-01234567890123456
      name: custom-ami-amd64
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values:
            - amd64
    - id: ami-01234567890123456
      name: custom-ami-arm64
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values:
            - arm64

  # Capacity Reservations
  capacityReservations:
    - availabilityZone: us-west-2a
      id: cr-01234567890123456
      instanceMatchCriteria: targeted
      instanceType: g6.48xlarge
      interruptible: false
      ownerID: "012345678901"
      reservationType: capacity-block
      state: expiring
    - availabilityZone: us-west-2c
      id: cr-12345678901234567
      instanceMatchCriteria: open
      instanceType: g6.48xlarge
      interruptible: true
      ownerID: "98765432109"
      reservationType: default
      state: active

  # Generated instance profile name from "role"
  instanceProfile: "${CLUSTER_NAME}-0123456778901234567789"
  conditions:
    - lastTransitionTime: "2024-02-02T19:54:34Z"
      status: "True"
      type: InstanceProfileReady
    - lastTransitionTime: "2024-02-02T19:54:34Z"
      status: "True"
      type: SubnetsReady
    - lastTransitionTime: "2024-02-02T19:54:34Z"
      status: "True"
      type: SecurityGroupsReady
    - lastTransitionTime: "2024-02-02T19:54:34Z"
      status: "True"
      type: AMIsReady
    - lastTransitionTime: "2024-02-02T19:54:34Z"
      status: "True"
      type: Ready
```
Refer to the [NodePool docs](/preview/concepts/nodepools/index.md) for settings applicable to all providers. To explore various `EC2NodeClass` configurations, refer to the examples provided [in the Karpenter Github repository](https://github.com/aws/karpenter/blob/main/examples/v1/).


## spec.kubelet

Karpenter provides the ability to specify a few additional Kubelet arguments.
These are all optional and provide support for additional customization and use cases.
Adjust these only if you know you need to do so.
For more details on kubelet settings, see the [KubeletConfiguration reference](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1/).
The implemented fields are a subset of the full list of upstream kubelet configuration arguments.

```yaml
kubelet:
  podsPerCore: 2
  maxPods: 20
  systemReserved:
    cpu: 100m
    memory: 100Mi
    ephemeral-storage: 1Gi
  kubeReserved:
    cpu: 200m
    memory: 100Mi
    ephemeral-storage: 3Gi
  evictionHard:
    memory.available: 5%
    nodefs.available: 10%
    nodefs.inodesFree: 10%
  evictionSoft:
    memory.available: 500Mi
    nodefs.available: 15%
    nodefs.inodesFree: 15%
  evictionSoftGracePeriod:
    memory.available: 1m
    nodefs.available: 1m30s
    nodefs.inodesFree: 2m
  evictionMaxPodGracePeriod: 60
  imageGCHighThresholdPercent: 85
  imageGCLowThresholdPercent: 80
  cpuCFSQuota: true
  clusterDNS: ["10.0.1.100"]
```



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    <p>If you need to specify a field that isn&rsquo;t present in <code>spec.kubelet</code>, you can set it via custom <a href="#specuserdata">UserData</a>.
For example, if you wanted to configure <code>maxPods</code> and <code>registryPullQPS</code> you would set the former through <code>spec.kubelet</code> and the latter through UserData.
The following example achieves this with AL2023:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">karpenter.k8s.aws/v1</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EC2NodeClass</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">amiSelectorTerms</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span>- <span style="color:#204a87;font-weight:bold">alias</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">al2023@v20240807</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">kubelet</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span><span style="color:#204a87;font-weight:bold">maxPods</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">42</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">userData</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">|</span><span style="color:#8f5902;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">    apiVersion: node.eks.aws/v1alpha1
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">    kind: NodeConfig
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">    spec:
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">      kubelet:
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">        config:
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">          # Configured through UserData since unavailable in `spec.kubelet`
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">          registryPullQPS: 10</span><span style="color:#f8f8f8;text-decoration:underline">    
</span></span></span></code></pre></div><p>Note that when using the <code>Custom</code> AMIFamily you will need to specify fields <strong>both</strong> in <code>spec.kubelet</code> and <code>spec.userData</code>.</p>


</div>


#### Pods Per Core

An alternative way to dynamically set the maximum density of pods on a node is to use the `.spec.kubelet.podsPerCore` value. Karpenter will calculate the pod density during scheduling by multiplying this value by the number of logical cores (vCPUs) on an instance type. This value will also be passed through to the `--pods-per-core` value on kubelet startup to configure the number of allocatable pods the kubelet can assign to the node instance.

The value generated from `podsPerCore` cannot exceed `maxPods`, meaning, if both are set, the minimum of the `podsPerCore` dynamic pod density and the static `maxPods` value will be used for scheduling.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    <code>maxPods</code> may not be set in the <code>kubelet</code> of an EC2NodeClass, but may still be restricted by the <code>ENI_LIMITED_POD_DENSITY</code> value. You may want to ensure that the <code>podsPerCore</code> value that will be used for instance families associated with the EC2NodeClass will not cause unexpected behavior by exceeding the <code>maxPods</code> value.

</div>


#### Max Pods

For small instances that require an increased pod density or large instances that require a reduced pod density, you can override this default value with `.spec.kubelet.maxPods`. This value will be used during Karpenter pod scheduling and passed through to `--max-pods` on kubelet startup.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    When using small instance types, it may be necessary to enable <a href="https://aws.amazon.com/blogs/containers/amazon-vpc-cni-increases-pods-per-node-limits/">prefix assignment mode</a> in the AWS VPC CNI plugin to support a higher pod density per node.  Prefix assignment mode was introduced in AWS VPC CNI v1.9 and allows ENIs to manage a broader set of IP addresses.  Much higher pod densities are supported as a result.

</div>




<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Windows Support Notice</h4>

    Presently, Windows worker nodes do not support using more than one ENI.
As a consequence, the number of IP addresses, and subsequently, the number of pods that a Windows worker node can support is limited by the number of IPv4 addresses available on the primary ENI.
Currently, Karpenter will only consider individual secondary IP addresses when calculating the pod density limit.

</div>


### Reserved Resources

Karpenter will automatically configure the system and kube reserved resource requests on the fly on your behalf. These requests are used to configure your node and to make scheduling decisions for your pods. If you have specific requirements or know that you will have additional capacity requirements, you can optionally override the `--system-reserved` configuration defaults with the `.spec.kubelet.systemReserved` values and the `--kube-reserved` configuration defaults with the `.spec.kubelet.kubeReserved` values.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    Karpenter considers these reserved resources when computing the allocatable ephemeral storage on a given instance type.
If <code>kubeReserved</code> is not specified, Karpenter will compute the default reserved <a href="https://github.com/awslabs/amazon-eks-ami/blob/db28da15d2b696bc08ac3aacc9675694f4a69933/files/bootstrap.sh#L251">CPU</a> and <a href="https://github.com/awslabs/amazon-eks-ami/blob/db28da15d2b696bc08ac3aacc9675694f4a69933/files/bootstrap.sh#L235">memory</a> resources for the purpose of ephemeral storage computation.
These defaults are based on the defaults on Karpenter&rsquo;s supported AMI families, which are not the same as the kubelet defaults.
You should be aware of the CPU and memory default calculation when using Custom AMI Families. If they don&rsquo;t align, there may be a difference in Karpenter&rsquo;s computed allocatable ephemeral storage and the actually ephemeral storage available on the node.

</div>


### Eviction Thresholds

The kubelet supports eviction thresholds by default. When enough memory or file system pressure is exerted on the node, the kubelet will begin to evict pods to ensure that system daemons and other system processes can continue to run in a healthy manner.

Kubelet has the notion of [hard evictions](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#hard-eviction-thresholds) and [soft evictions](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#soft-eviction-thresholds). In hard evictions, pods are evicted as soon as a threshold is met, with no grace period to terminate. Soft evictions, on the other hand, provide an opportunity for pods to be terminated gracefully. They do so by sending a termination signal to pods that are planning to be evicted and allowing those pods to terminate up to their grace period.

Karpenter supports [hard evictions](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#hard-eviction-thresholds) through the `.spec.kubelet.evictionHard` field and [soft evictions](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#soft-eviction-thresholds) through the `.spec.kubelet.evictionSoft` field. `evictionHard` and `evictionSoft` are configured by listing [signal names](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#eviction-signals) with either percentage values or resource values.

```yaml
kubelet:
  evictionHard:
    memory.available: 500Mi
    nodefs.available: 10%
    nodefs.inodesFree: 10%
    imagefs.available: 5%
    imagefs.inodesFree: 5%
    pid.available: 7%
  evictionSoft:
    memory.available: 1Gi
    nodefs.available: 15%
    nodefs.inodesFree: 15%
    imagefs.available: 10%
    imagefs.inodesFree: 10%
    pid.available: 10%
```

#### Supported Eviction Signals

| Eviction Signal    | Description                                                                     |
|--------------------|---------------------------------------------------------------------------------|
| memory.available   | memory.available := node.status.capacity[memory] - node.stats.memory.workingSet |
| nodefs.available   | nodefs.available := node.stats.fs.available                                     |
| nodefs.inodesFree  | nodefs.inodesFree := node.stats.fs.inodesFree                                   |
| imagefs.available  | imagefs.available := node.stats.runtime.imagefs.available                       |
| imagefs.inodesFree | imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree                     |
| pid.available      | pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc           |

For more information on eviction thresholds, view the [Node-pressure Eviction](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction) section of the official Kubernetes docs.

#### Soft Eviction Grace Periods

Soft eviction pairs an eviction threshold with a specified grace period. With soft eviction thresholds, the kubelet will only begin evicting pods when the node exceeds its soft eviction threshold over the entire duration of its grace period. For example, if you specify `evictionSoft[memory.available]` of `500Mi` and a `evictionSoftGracePeriod[memory.available]` of `1m30`, the node must have less than `500Mi` of available memory over a minute and a half in order for the kubelet to begin evicting pods.

Optionally, you can specify an `evictionMaxPodGracePeriod` which defines the administrator-specified maximum pod termination grace period to use during soft eviction. If a namespace-owner had specified a pod `terminationGracePeriodInSeconds` on pods in their namespace, the minimum of `evictionPodGracePeriod` and `terminationGracePeriodInSeconds` would be used.

```yaml
kubelet:
  evictionSoftGracePeriod:
    memory.available: 1m
    nodefs.available: 1m30s
    nodefs.inodesFree: 2m
    imagefs.available: 1m30s
    imagefs.inodesFree: 2m
    pid.available: 2m
  evictionMaxPodGracePeriod: 60
```

### Pod Density

By default, the number of pods on a node is limited by both the number of networking interfaces (ENIs) that may be attached to an instance type and the number of IP addresses that can be assigned to each ENI.  See [IP addresses per network interface per instance type](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html#AvailableIpPerENI) for a more detailed information on these instance types' limits.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    By default, the VPC CNI allocates IPs for a node and pods from the same subnet. With <a href="https://aws.github.io/aws-eks-best-practices/networking/custom-networking">VPC CNI Custom Networking</a>, the pods will receive IP addresses from another subnet dedicated to pod IPs. This approach makes it easier to manage IP addresses and allows for separate Network Access Control Lists (NACLs) applied to your pods. VPC CNI Custom Networking reduces the pod density of a node since one of the ENI attachments will be used for the node and cannot share the allocated IPs on the interface to pods. Karpenter supports VPC CNI Custom Networking and similar CNI setups where the primary node interface is separated from the pods interfaces through a global environment variable RESERVED_ENIS, see <a href="/preview/reference/settings/index.md">Settings</a>. In the common case, RESERVED_ENIS should be set to &ldquo;1&rdquo; if using Custom Networking.

</div>




<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Windows Support Notice</h4>

    It&rsquo;s currently not possible to specify custom networking with Windows nodes.

</div>


## spec.amiFamily

AMIFamily dictates the default bootstrapping logic for nodes provisioned through this `EC2NodeClass`.
An `amiFamily` is only required if you don't specify a `spec.amiSelectorTerms.alias` object.
For example, if you specify `alias: al2023@v20240807`, the `amiFamily` is implicitly `AL2023`.

AMIFamily does not impact which AMI is discovered, only the UserData generation and default BlockDeviceMappings. To automatically discover EKS optimized AMIs, use the new [`alias` field in amiSelectorTerms](#specamiselectorterms).



<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Ubuntu Support Dropped at v1</h4>

    <p>Support for the Ubuntu AMIFamily has been dropped at Karpenter <code>v1.0.0</code>.
This means Karpenter no longer supports automatic AMI discovery and UserData generation for Ubuntu.
To continue using Ubuntu AMIs, you will need to select Ubuntu AMIs using <code>amiSelectorTerms</code>.</p>
<p>Additionally, you will need to either maintain UserData yourself using the <code>Custom</code> AMIFamily, or you can use the <code>AL2</code> AMIFamily and custom <code>blockDeviceMappings</code> (as shown below).
The <code>AL2</code> family has an identical UserData format, but this compatibility isn&rsquo;t guaranteed long term.
Changes to AL2&rsquo;s or Ubuntu&rsquo;s UserData format could result in incompatibility, at which point the <code>Custom</code> AMIFamily must be used.</p>
<p><strong>Ubuntu NodeClass Example:</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">karpenter.k8s.aws/v1</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EC2NodeClass</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">amiFamily</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">AL2</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">amiSelectorTerms</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span>- <span style="color:#204a87;font-weight:bold">id</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">ami-placeholder</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">blockDeviceMappings</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span>- <span style="color:#204a87;font-weight:bold">deviceName</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;/dev/sda1&#39;</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span><span style="color:#204a87;font-weight:bold">rootVolume</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">    </span><span style="color:#204a87;font-weight:bold">ebs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">      </span><span style="color:#204a87;font-weight:bold">encrypted</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">      </span><span style="color:#204a87;font-weight:bold">volumeType</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">gp3</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">      </span><span style="color:#204a87;font-weight:bold">volumeSize</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">20Gi</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span></code></pre></div>

</div>



### AL2



<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">AL2 support dropped at Kubernetes 1.33</h4>

    Kubernetes version 1.32 is the last version for which Amazon EKS will release Amazon Linux 2 (AL2) AMIs.

</div>




<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    Note that Karpenter will automatically generate a call to the <code>/etc/eks/bootstrap.sh</code> script as part of its generated UserData. When using <code>amiFamily: AL2</code> you should not call this script yourself in <code>.spec.userData</code>. If you need to, use the <a href="/preview/concepts/nodeclasses/index.md#custom">Custom AMI family</a> instead.

</div>


```bash
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
/etc/eks/bootstrap.sh 'test-cluster' --apiserver-endpoint 'https://test-cluster' --b64-cluster-ca 'ca-bundle' \
--dns-cluster-ip '10.100.0.10' \
--use-max-pods false \
--kubelet-extra-args '--node-labels=karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test  --max-pods=110'
--//--
```

### AL2023

```text
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: application/node.eks.aws

# Karpenter Generated NodeConfig
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    name: test-cluster
    apiServerEndpoint: https://example.com
    certificateAuthority: ca-bundle
    cidr: 10.100.0.0/16
  kubelet:
    config:
      maxPods: 110
    flags:
      - --node-labels=karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test

--//--

```

### Bottlerocket

```toml
[settings]
[settings.kubernetes]
api-server = 'https://test-cluster'
cluster-certificate = 'ca-bundle'
cluster-name = 'test-cluster'
cluster-dns-ip = '10.100.0.10'
max-pods = 110

[settings.kubernetes.node-labels]
'karpenter.sh/capacity-type' = 'on-demand'
'karpenter.sh/nodepool' = 'test'
```

### Windows2019

```powershell
<powershell>
[string]$EKSBootstrapScriptFile = "$env:ProgramFiles\Amazon\EKS\Start-EKSBootstrap.ps1"
& $EKSBootstrapScriptFile -EKSClusterName 'test-cluster' -APIServerEndpoint 'https://test-cluster' -Base64ClusterCA 'ca-bundle' -KubeletExtraArgs '--node-labels="karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test" --max-pods=110' -DNSClusterIP '10.100.0.10'
</powershell>
```

### Windows2022

```powershell
<powershell>
[string]$EKSBootstrapScriptFile = "$env:ProgramFiles\Amazon\EKS\Start-EKSBootstrap.ps1"
& $EKSBootstrapScriptFile -EKSClusterName 'test-cluster' -APIServerEndpoint 'https://test-cluster' -Base64ClusterCA 'ca-bundle' -KubeletExtraArgs '--node-labels="karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test" --max-pods=110' -DNSClusterIP '10.100.0.10'
</powershell>
```

### Windows2025

```powershell
<powershell>
[string]$EKSBootstrapScriptFile = "$env:ProgramFiles\Amazon\EKS\Start-EKSBootstrap.ps1"
& $EKSBootstrapScriptFile -EKSClusterName 'test-cluster' -APIServerEndpoint 'https://test-cluster' -Base64ClusterCA 'ca-bundle' -KubeletExtraArgs '--node-labels="karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test" --max-pods=110' -DNSClusterIP '10.100.0.10'
</powershell>
```

### Custom

The `Custom` AMIFamily ships without any default userData to allow you to configure custom bootstrapping for control planes or images that don't support the default methods from the other families. For this AMIFamily, kubelet must add the taint `karpenter.sh/unregistered:NoExecute` via the `--register-with-taints` flag ([flags](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/#options)) or the KubeletConfiguration spec ([options](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1/#kubelet-config-k8s-io-v1-CredentialProviderConfig) and [docs](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/)). Karpenter will fail to register nodes that do not have this taint.

## spec.subnetSelectorTerms

Subnet Selector Terms allow you to specify selection logic for a set of subnet options that Karpenter can choose from when launching an instance from the `EC2NodeClass`. Karpenter discovers subnets through the `EC2NodeClass` using ids or [tags](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html). When launching nodes, a subnet is automatically chosen that matches the desired zone. If multiple subnets exist for a zone, the one with the most available IP addresses will be used.

This selection logic is modeled as terms, where each term contains multiple conditions that must all be satisfied for the selector to match. Effectively, all requirements within a single term are ANDed together. It's possible that you may want to select on two different subnets that have unrelated requirements. In this case, you can specify multiple terms which will be ORed together to form your selection logic. The example below shows how this selection logic is fulfilled.

```yaml
subnetSelectorTerms:
  # Select on any subnet that has the "karpenter.sh/discovery: ${CLUSTER_NAME}"
  # AND the "environment: test" tag OR any subnet with ID "subnet-09fa4a0a8f233a921"
  - tags:
      karpenter.sh/discovery: "${CLUSTER_NAME}"
      environment: test
  - id: subnet-09fa4a0a8f233a921
```



<div class="alert alert-secondary" role="alert">
<h4 class="alert-heading">Tip</h4>

    Subnets may be specified by any tag, including <code>Name</code>. Selecting tag values using wildcards (<code>*</code>) is supported.

</div>


#### Examples

Select all with a specified tag key:
```yaml
spec:
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery/MyClusterName: '*'
```

Select by name and tag (all criteria must match):
```yaml
spec:
  subnetSelectorTerms:
    - tags:
        Name: my-subnet
        MyTag: '' # matches all resources with the tag
```

Select using multiple tag terms:
```yaml
spec:
  subnetSelectorTerms:
    - tags:
        Name: "my-subnet-1"
    - tags:
        Name: "my-subnet-2"
```

Select using wildcards:
```yaml
spec:
  subnetSelectorTerms:
    - tags:
        Name: "*Public*"

```

Select using ids:
```yaml
spec:
  subnetSelectorTerms:
    - id: "subnet-09fa4a0a8f233a921"
    - id: "subnet-0471ca205b8a129ae"
```


## spec.securityGroupSelectorTerms

Security Group Selector Terms allow you to specify selection logic for all security groups that will be attached to an instance launched from the `EC2NodeClass`. The security group of an instance is comparable to a set of firewall rules.
[EKS creates at least two security groups by default](https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html).

This selection logic is modeled as terms, where each term contains multiple conditions that must all be satisfied for the selector to match. Effectively, all requirements within a single term are ANDed together. It's possible that you may want to select on two different security groups that have unrelated requirements. In this case, you can specify multiple terms which will be ORed together to form your selection logic. The example below shows how this selection logic is fulfilled.

```yaml
securityGroupSelectorTerms:
  # Select on any security group that has both the "karpenter.sh/discovery: ${CLUSTER_NAME}" tag
  # AND the "environment: test" tag OR any security group with the "my-security-group" name
  # OR any security group with ID "sg-063d7acfb4b06c82c"
  - tags:
      karpenter.sh/discovery: "${CLUSTER_NAME}"
      environment: test
  - name: my-security-group
  - id: sg-063d7acfb4b06c82c
```



<div class="alert alert-secondary" role="alert">
<h4 class="alert-heading">Tip</h4>

    Security groups may be specified by any tag, including &ldquo;Name&rdquo;. Selecting tags using wildcards (<code>*</code>) is supported.

</div>




<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    <p>When launching nodes, Karpenter uses all the security groups that match the selector. If you choose to use the <code>kubernetes.io/cluster/$CLUSTER_NAME</code> tag for discovery, note that this may result in failures using the AWS Load Balancer controller. The Load Balancer controller only supports a single security group having that tag key. See <a href="https://github.com/kubernetes-sigs/aws-load-balancer-controller/issues/2367">this issue</a> for more details.</p>
<p>To verify if this restriction affects you, run the following commands.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#000">CLUSTER_VPC_ID</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;</span><span style="color:#204a87;font-weight:bold">$(</span>aws eks describe-cluster --name <span style="color:#000">$CLUSTER_NAME</span> --query cluster.resourcesVpcConfig.vpcId --output text<span style="color:#204a87;font-weight:bold">)</span><span style="color:#4e9a06">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>aws ec2 describe-security-groups --filters <span style="color:#000">Name</span><span style="color:#ce5c00;font-weight:bold">=</span>vpc-id,Values<span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$CLUSTER_VPC_ID</span> <span style="color:#000">Name</span><span style="color:#ce5c00;font-weight:bold">=</span>tag-key,Values<span style="color:#ce5c00;font-weight:bold">=</span>kubernetes.io/cluster/<span style="color:#000">$CLUSTER_NAME</span> --query <span style="color:#4e9a06">&#39;SecurityGroups[].[GroupName]&#39;</span> --output text
</span></span></code></pre></div><p>If multiple securityGroups are printed, you will need more specific securityGroupSelectorTerms. We generally recommend that you use the <code>karpenter.sh/discovery: $CLUSTER_NAME</code> tag selector instead.</p>


</div>


#### Examples

Select all assigned to a cluster:
```yaml
spec:
  securityGroupSelectorTerms:
    - tags:
        kubernetes.io/cluster/$CLUSTER_NAME: "owned"
```

Select all with a specified tag key:
```yaml
spec:
  securityGroupSelectorTerms:
    - tags:
        MyTag: '*'
```

Select by name and tag (all criteria must match):
```yaml
spec:
  securityGroupSelectorTerms:
    - name: my-security-group
      tags:
        MyTag: '*' # matches all resources with the tag
```

Select using multiple tag terms:
```yaml
spec:
  securityGroupSelectorTerms:
    - tags:
        Name: "my-security-group-1"
    - tags:
        Name: "my-security-group-2"
```

Select by name using a wildcard:
```yaml
spec:
  securityGroupSelectorTerms:
    - name: "*Public*"
```

Select using ids:
```yaml
spec:
 securityGroupSelectorTerms:
    - id: "sg-063d7acfb4b06c82c"
    - id: "sg-06e0cf9c198874591"
```

## spec.role

`Role` is an optional field and tells Karpenter which IAM identity nodes should assume. You must specify one of `role` or `instanceProfile` when creating a Karpenter `EC2NodeClass`. If using the [Karpenter Getting Started Guide](/preview/getting-started/getting-started-with-karpenter/index.md) to deploy Karpenter, you can use the `KarpenterNodeRole-$CLUSTER_NAME` role provisioned by that process.

```yaml
spec:
  role: "KarpenterNodeRole-$CLUSTER_NAME"
```

## spec.instanceProfile

`InstanceProfile` is an optional field and tells Karpenter which IAM identity nodes should assume. You must specify one of `role` or `instanceProfile` when creating a Karpenter `EC2NodeClass`. If you use the `instanceProfile` field instead of `role`, Karpenter will not manage the InstanceProfile on your behalf; instead, it expects that you have pre-provisioned an IAM instance profile and assigned it a role.

You can provision and assign a role to an IAM instance profile using [CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html) or by using the [`aws iam create-instance-profile`](https://docs.aws.amazon.com/cli/latest/reference/iam/create-instance-profile.html) and [`aws iam add-role-to-instance-profile`](https://docs.aws.amazon.com/cli/latest/reference/iam/add-role-to-instance-profile.html) commands in the CLI.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    For <a href="https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html">private clusters</a> without access to their AWS region&rsquo;s IAM API endpoint, using <code>spec.instanceProfile</code> is required. <code>spec.role</code> cannot be used since Karpenter needs to access IAM endpoints to manage a generated instance profile. IAM <a href="https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html">doesn&rsquo;t support private endpoints</a> to enable accessing the service without going to the public internet.

</div>




<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>

    When using <code>spec.instanceProfile</code>, ensure you are using pre-provisioned instance profiles that you manage yourself.

</div>


## spec.amiSelectorTerms

AMI Selector Terms are __required__ and are used to configure AMIs for Karpenter to use. AMIs are discovered through alias, id, owner, name, and [tags](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html).

This selection logic is modeled as terms, where each term contains multiple conditions that must all be satisfied for the selector to match.
Effectively, all requirements within a single term are ANDed together.
It's possible that you may want to select on two different AMIs that have unrelated requirements.
In this case, you can specify multiple terms which will be ORed together to form your selection logic.
The example below shows how this selection logic is fulfilled.

```yaml
amiSelectorTerms:
  # Select on any AMI that has both the `karpenter.sh/discovery: ${CLUSTER_NAME}`
  # AND `environment: test` tags OR any AMI with the name `my-ami` OR an AMI with
  # ID `ami-123` OR an AMI with ID matching the value of my-custom-parameter
  - tags:
      karpenter.sh/discovery: "${CLUSTER_NAME}"
      environment: test
  - name: my-ami
  - id: ami-123
  - ssmParameter: my-custom-parameter # ssm parameter name or ARN
  # Select EKS optimized AL2023 AMIs with version `v20240807`. This term is mutually
  # exclusive and can't be specified with other terms.
  # - alias: al2023@v20240807
```

An `alias` term can be used to select EKS-optimized AMIs. An `alias` is formatted as `family@version`. Family can be one of the following values:

* `al2`
* `al2023`
* `bottlerocket`
* `windows2019`
* `windows2022`
* `windows2025`

The version string can be set to `latest`, or pinned to a specific AMI using the format of that AMI's GitHub release tags.
For example, AL2 and AL2023 use dates for their release, so they can be pinned as follows:
```yaml
alias: al2023@v20240703
```
Bottlerocket uses a semantic version for their releases. You can pin bottlerocket as follows:
```yaml
alias: bottlerocket@v1.20.4
```
The Windows family does not support pinning, so only `latest` is supported.

The following commands can be used to determine the versions availble for an alias in your region:


  
  
  
<ul class="nav nav-tabs" id="tabs-18" role="tablist">
  <li class="nav-item">
      <a class="nav-link active"
          id="tabs-18-00-tab" data-toggle="tab" href="#tabs-18-00" role="tab"
          aria-controls="tabs-18-00" aria-selected="false">
        AL2023
      </a>
    </li><li class="nav-item">
      <a class="nav-link"
          id="tabs-18-01-tab" data-toggle="tab" href="#tabs-18-01" role="tab"
          aria-controls="tabs-18-01" aria-selected="false">
        AL2
      </a>
    </li><li class="nav-item">
      <a class="nav-link"
          id="tabs-18-02-tab" data-toggle="tab" href="#tabs-18-02" role="tab"
          aria-controls="tabs-18-02" aria-selected="false">
        Bottlerocket
      </a>
    </li>
</ul>

<div class="tab-content" id="tabs-18-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-18-00" role="tabpanel" aria-labelled-by="tabs-18-00-tab">
        <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">K8S_VERSION</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1.36&#34;</span>
</span></span><span style="display:flex;"><span>aws ssm get-parameters-by-path --path <span style="color:#4e9a06">&#34;/aws/service/eks/optimized-ami/</span><span style="color:#000">$K8S_VERSION</span><span style="color:#4e9a06">/amazon-linux-2023/&#34;</span> --recursive <span style="color:#000;font-weight:bold">|</span> jq -cr <span style="color:#4e9a06">&#39;.Parameters[].Name&#39;</span> <span style="color:#000;font-weight:bold">|</span> grep -v <span style="color:#4e9a06">&#34;recommended&#34;</span> <span style="color:#000;font-weight:bold">|</span> awk -F <span style="color:#4e9a06">&#39;/&#39;</span> <span style="color:#4e9a06">&#39;{print $10}&#39;</span> <span style="color:#000;font-weight:bold">|</span> sed -r <span style="color:#4e9a06">&#39;s/.*(v[[:digit:]]+)$/\1/&#39;</span> <span style="color:#000;font-weight:bold">|</span> sort <span style="color:#000;font-weight:bold">|</span> uniq
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-18-01" role="tabpanel" aria-labelled-by="tabs-18-01-tab">
        <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">K8S_VERSION</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1.36&#34;</span>
</span></span><span style="display:flex;"><span>aws ssm get-parameters-by-path --path <span style="color:#4e9a06">&#34;/aws/service/eks/optimized-ami/</span><span style="color:#000">$K8S_VERSION</span><span style="color:#4e9a06">/amazon-linux-2/&#34;</span> --recursive <span style="color:#000;font-weight:bold">|</span> jq -cr <span style="color:#4e9a06">&#39;.Parameters[].Name&#39;</span> <span style="color:#000;font-weight:bold">|</span> grep -v <span style="color:#4e9a06">&#34;recommended&#34;</span> <span style="color:#000;font-weight:bold">|</span> awk -F <span style="color:#4e9a06">&#39;/&#39;</span> <span style="color:#4e9a06">&#39;{print $8}&#39;</span> <span style="color:#000;font-weight:bold">|</span> sed -r <span style="color:#4e9a06">&#39;s/.*(v[[:digit:]]+)$/\1/&#39;</span> <span style="color:#000;font-weight:bold">|</span> sort <span style="color:#000;font-weight:bold">|</span> uniq
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-18-02" role="tabpanel" aria-labelled-by="tabs-18-02-tab">
        <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">K8S_VERSION</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1.36&#34;</span>
</span></span><span style="display:flex;"><span>aws ssm get-parameters-by-path --path <span style="color:#4e9a06">&#34;/aws/service/bottlerocket/aws-k8s-</span><span style="color:#000">$K8S_VERSION</span><span style="color:#4e9a06">&#34;</span> --recursive <span style="color:#000;font-weight:bold">|</span> jq -cr <span style="color:#4e9a06">&#39;.Parameters[].Name&#39;</span> <span style="color:#000;font-weight:bold">|</span> grep -v <span style="color:#4e9a06">&#34;latest&#34;</span> <span style="color:#000;font-weight:bold">|</span> awk -F <span style="color:#4e9a06">&#39;/&#39;</span> <span style="color:#4e9a06">&#39;{print $7}&#39;</span> <span style="color:#000;font-weight:bold">|</span> sort <span style="color:#000;font-weight:bold">|</span> uniq
</span></span></code></pre></div>
    </div>
</div>




<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>

    Karpenter supports automatic AMI selection and upgrades using the <code>latest</code> version pin, but this is <strong>not</strong> recommended for production environments.
When using <code>latest</code>, a new AMI release will cause Karpenter to drift all out-of-date nodes in the cluster, replacing them with nodes running the new AMI.
We strongly recommend evaluating new AMIs in a lower environment before rolling them out into a production environment.
More details on Karpenter&rsquo;s recommendations for managing AMIs can be found <a href="/preview/tasks/managing-amis/index.md">here</a>.

</div>


To select an AMI by name, use the `name` field in the selector term. To select an AMI by id, use the `id` field in the selector term. To select AMIs that are not owned by `amazon` or the account that Karpenter is running in, use the `owner` field - you can use a combination of account aliases (e.g. `self` `amazon`, `your-aws-account-name`) and account IDs.

If owner is not set for `name`, it defaults to `self,amazon`, preventing Karpenter from inadvertently selecting an AMI that is owned by a different account. Tags don't require an owner as tags can only be discovered by the user who created them.



<div class="alert alert-secondary" role="alert">
<h4 class="alert-heading">Tip</h4>

    AMIs may be specified by any AWS tag, including <code>Name</code>. Selecting by tag or by name using wildcards (<code>*</code>) is supported.

</div>




<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    <p>If <code>amiSelectorTerms</code> match more than one AMI, Karpenter will automatically determine which AMI best fits the workloads on the launched worker node under the following constraints:</p>
<ul>
<li>When launching nodes, Karpenter automatically determines which architecture a custom AMI is compatible with and will use images that match an instanceType&rsquo;s requirements.
<ul>
<li>Unless using an alias, Karpenter <strong>cannot</strong> detect requirements other than architecture. If you need to specify different AMIs for different kind of nodes (e.g. accelerated GPU AMIs), you should use a separate <code>EC2NodeClass</code>.</li>
</ul>
</li>
<li>If multiple AMIs are found that can be used, Karpenter will choose the latest one.</li>
<li>If no AMIs are found that can be used, then no nodes will be provisioned.</li>
</ul>


</div>


#### Examples

Select by AMI family and version:
```yaml
  amiSelectorTerms:
    - alias: al2023@v20240807
```

Select all with a specified tag:

```yaml
  amiSelectorTerms:
    - tags:
        karpenter.sh/discovery/MyClusterName: '*'
```

Select by name:
```yaml
  amiSelectorTerms:
    - name: my-ami
```

Select by `Name` tag:
```yaml
  amiSelectorTerms:
    - tags:
        Name: my-ami
```

Select by name and owner:
```yaml
  amiSelectorTerms:
    - name: my-ami
      owner: self
    - name: my-ami
      owner: "0123456789"
```

Select by name using a wildcard:
```yaml
spec:
  amiSelectorTerms:
    - name: "*EKS*"
```

Select by all under an owner:
```yaml
spec:
  amiSelectorTerms:
    - name: "*"
      owner: self
```

Specify using ids:
```yaml
  amiSelectorTerms:
    - id: "ami-123"
    - id: "ami-456"
```

Specify using custom ssm parameter name or ARN:
```yaml
  amiSelectorTerms:
    - ssmParameter: "my-custom-parameter"
```



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    When using a custom SSM parameter, you&rsquo;ll need to expand the <code>ssm:GetParameter</code> permissions on the Karpenter IAM role to include your custom parameter, as the default policy only allows access to the AWS public parameters.

</div>


## spec.capacityReservationSelectorTerms

<i class="fa-solid fa-circle-info"></i> <b>Feature State: </b> [Beta](/preview/reference/settings/index.md#feature-gates)

Capacity Reservation Selector Terms allow you to select [on-demand capacity reservations](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-capacity-reservations.html) (ODCRs), which will be made available to NodePools which select the given EC2NodeClass.
Karpenter will prioritize utilizing the capacity in these reservations before falling back to on-demand and spot.
Capacity reservations can be discovered using ids, tags, or instance match criteria.

This selection logic is modeled as terms.
A term can specify an ID, a set of tags, or instance match criteria to select against.
When specifying tags, it will select all capacity reservations accessible from the account with matching tags.
When specifying instance match criteria, it selects reservations by their matching behavior: `open` (matches all compatible instances) or `targeted` (matches only explicitly targeted instances).
This can be further restricted by specifying an owner ID.

For more information on utilizing ODCRs with Karpenter, refer to the [Utilizing ODCRs Task](/preview/tasks/odcrs/index.md).



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    Note that the IAM role Karpenter assumes should have a permissions policy associated with it that grants it permissions to use the <a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html#amazonec2-DescribeCapacityReservations">ec2:DescribeCapacityReservations</a> action to discover capacity reservations and the <a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html#amazonec2-RunInstances">ec2:RunInstances</a> action to run instances in those capacity reservations.

</div>


#### Examples

Select the reservations with the given IDs:

```yaml
spec:
  capacityReservationSelectorTerms:
  - id: cr-123
  - id: cr-456
```

Select the reservations by tags:

```yaml
spec:
  capacityReservationSelectorTerms:
  # Select all capacity reservations which have both matching tags
  - tags:
      key1: foo
      key2: bar
  # Additionally, select all capacity reservations with the following matching tag
  - tags:
      key3: foobar
```

Select by tags and owner ID:

```yaml
spec:
  # Select all capacity reservations with the matching tags which are also owned by
  # the specified account.
  capacityReservationSelectorTerms:
  - tags:
      key: foo
    ownerID: 012345678901
```

Select by instance match criteria:

```yaml
spec:
  capacityReservationSelectorTerms:
  # Select all open capacity reservations
  - instanceMatchCriteria: open
```

Select by instance match criteria and tags:

```yaml
spec:
  capacityReservationSelectorTerms:
  # Select targeted capacity reservations with matching tags
  - instanceMatchCriteria: targeted
    tags:
      key: foo
```

## spec.placementGroupSelector

Placement Group Selector allows you to select a [placement group](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html) for instances launched by this EC2NodeClass. Each EC2NodeClass maps to exactly one placement group — all instances launched from that EC2NodeClass are placed into the resolved placement group.

Placement groups can be selected by either name or ID. Only one of `name` or `id` may be specified.

Karpenter supports all three placement group strategies:
- **Cluster** — instances are placed in a single AZ on the same network segment for low-latency, high-throughput networking (e.g., EFA workloads)
- **Partition** — instances are distributed across isolated partitions (up to 7 per AZ) for hardware fault isolation. Applications can use `topologySpreadConstraints` with the `karpenter.k8s.aws/placement-group-partition` label to spread workloads across partitions.
- **Spread** — each instance is placed on distinct hardware (up to 7 instances per AZ per group) for maximum fault isolation



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    The IAM role Karpenter assumes must have permissions for the <a href="https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribePlacementGroups.html">ec2:DescribePlacementGroups</a> action to discover placement groups and the <a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html#amazonec2-RunInstances">ec2:RunInstances</a> / <a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html#amazonec2-CreateFleet">ec2:CreateFleet</a> actions to launch instances into the placement group.

</div>


#### Examples

Select the placement group with the given ID:

```yaml
spec:
  placementGroupSelector:
    id: pg-123
```

Select the placement group with the given name:

```yaml
spec:
  placementGroupSelector:
    name: my-pg-a
```

## spec.tags

Karpenter adds tags to all resources it creates, including EC2 Instances, EBS volumes, and Launch Templates. The default set of tags are listed below.

```yaml
Name: <node-name>
karpenter.sh/nodeclaim: <nodeclaim-name>
karpenter.sh/nodepool: <nodepool-name>
karpenter.k8s.aws/ec2nodeclass: <ec2nodeclass-name>
kubernetes.io/cluster/<cluster-name>: owned
eks:eks-cluster-name: <cluster-name>
```

Additional tags can be added in the tags section, which will be merged with the default tags specified above.
```yaml
spec:
  tags:
    InternalAccountingTag: 1234
    dev.corp.net/app: Calculator
    dev.corp.net/team: MyTeam
```



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    Karpenter allows overrides of the default &ldquo;Name&rdquo; tag but does not allow overrides to restricted domains (such as &ldquo;karpenter.sh&rdquo;, &ldquo;karpenter.k8s.aws&rdquo;, and &ldquo;kubernetes.io/cluster&rdquo;). This ensures that Karpenter is able to correctly auto-discover nodes that it owns.

</div>


## spec.metadataOptions

Control the exposure of [Instance Metadata Service](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) on EC2 Instances launched by this EC2NodeClass using a generated launch template.

Refer to [recommended, security best practices](https://aws.github.io/aws-eks-best-practices/security/docs/iam/#restrict-access-to-the-instance-profile-assigned-to-the-worker-node) for limiting exposure of Instance Metadata and User Data to pods.

If metadataOptions are omitted from this EC2NodeClass, the following default settings are applied:

```yaml
spec:
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 1
    httpTokens: required
```

## spec.connectionTracking

Configure [Connection Tracking Timeouts](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html#connection-tracking-timeouts)
on the ENIs Karpenter provisions in the launch template: the primary ENI, any EFA ENIs, and user-configured `interface`
type network interfaces. EFA-only (`efa-only`) interfaces do not receive connection tracking settings. Secondary ENIs
created at runtime by your CNI (e.g. VPC-CNI, Cilium in ENI IPAM mode) are out of scope and must be configured through
the CNI. For VPC-CNI, see [aws/amazon-vpc-cni-k8s#3666](https://github.com/aws/amazon-vpc-cni-k8s/pull/3666) for
support replicating connection tracking settings to secondary ENIs.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    Connection tracking timeout configuration requires instances built on the <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html#ec2-nitro-instances">Nitro System</a>. When this field is set, Karpenter automatically excludes non-Nitro instance types from consideration.

</div>


Idle connections left too long can exhaust the security group's connection tracking table and lead to dropped packets.

All timeout values are specified in seconds as integers.
Any field left unset falls back to the EC2 default for that timeout.

### TCP Established Timeout
Timeout for idle TCP connections in an established state.
Value must be between 60 and 432,000 seconds (5 days). The default is 350 seconds for Nitro v6 instance types (excluding P6e-GB200) and 432,000 seconds for other instance types.
Setting a lower timeout helps free up tracking slots sooner at the cost of tearing down longer-lived TCP flows.

### UDP Stream Timeout
Timeout for idle UDP "stream" flows that have seen more than one request-response transaction.
Value must be between 60 and 180 seconds. AWS API defaults to 180 seconds.
Use a lower timeout to reclaim slots for true streaming traffic that stalls frequently.

### UDP Timeout
Timeout for idle UDP flows that have seen traffic only in a single direction or a single request-response transaction.
Value must be between 30 and 60 seconds. AWS API defaults to 30 seconds.
For high volume short-lived, stateless UDP transactions it may be preferable to use a lower timeout.

## spec.blockDeviceMappings

The `blockDeviceMappings` field in an `EC2NodeClass` can be used to control the [Elastic Block Storage (EBS) volumes](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html#instance-block-device-mapping) that Karpenter attaches to provisioned nodes. Karpenter uses default block device mappings for the AMIFamily specified. For example, the `Bottlerocket` AMI Family defaults with two block device mappings, one for Bottlerocket's control volume and the other for container resources such as images and logs.

```yaml
spec:
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
        iops: 10000
        encrypted: true
        kmsKeyID: "1234abcd-12ab-34cd-56ef-1234567890ab"
        deleteOnTermination: true
        throughput: 125
        snapshotID: snap-0123456789
        volumeInitializationRate: 100
```

The following blockDeviceMapping defaults are used for each `AMIFamily` if no `blockDeviceMapping` overrides are specified in the `EC2NodeClass`

### AL2
```yaml
spec:
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 20Gi
        volumeType: gp3
        encrypted: true
```

### AL2023
```yaml
spec:
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 20Gi
        volumeType: gp3
        encrypted: true
```

### Bottlerocket
```yaml
spec:
  blockDeviceMappings:
    # Root device
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 4Gi
        volumeType: gp3
        encrypted: true
    # Data device: Container resources such as images and logs
    - deviceName: /dev/xvdb
      ebs:
        volumeSize: 20Gi
        volumeType: gp3
        encrypted: true
```

### Windows2019/Windows2022/Windows2025
```yaml
spec:
  blockDeviceMappings:
    - deviceName: /dev/sda1
      ebs:
        volumeSize: 50Gi
        volumeType: gp3
        encrypted: true
```

### Custom

The `Custom` AMIFamily ships without any default `blockDeviceMappings`.

## spec.networkInterfaces

The `networkInterfaces` field allows you to configure network interface attachments for instances, including support for EFA (Elastic Fabric Adapter) devices for high-performance computing and machine learning workloads. For more information see the [AWS EFA docs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html).

Configure network interfaces by specifying the network card index, device index, and interface type:

```yaml
spec:
  networkInterfaces:
    - networkCardIndex: 0
      deviceIndex: 0
      interfaceType: "interface"
    - networkCardIndex: 0
      deviceIndex: 1
      interfaceType: "efa-only"
```

### Interface Types

- __interface__: Standard ENA (Elastic Network Adapter) interface providing IP connectivity
- __efa-only__: EFA interface that provides only the EFA device for RDMA communication without consuming an IP address

## spec.instanceStorePolicy

The `instanceStorePolicy` field controls how [instance-store](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html) volumes are handled. By default, Karpenter and Kubernetes will simply ignore them.

### RAID0

If you intend to use these volumes for faster node ephemeral-storage, set `instanceStorePolicy` to `RAID0`:

```yaml
spec:
  instanceStorePolicy: RAID0
```

This will set the allocatable ephemeral-storage of each node to the total size of the instance-store volume(s). This configuration is likely to be useful for workloads that leverage dense storage instance types or require the low latency from instance-stores that are nvme ssd based.

The disks must be formatted & mounted in a RAID0 and be the underlying filesystem for the Kubelet & Containerd. Even if you already configure your volumes with RAID0, Karpenter won't recognize this by default unless you set the `instanceStorePolicy` to `RAID0`. Without this, scheduling workloads that depend on ephemeral-storage from the instance-stores may result in a deadlock due to insufficient storage.

Instructions for each AMI family are listed below:

#### AL2

On AL2, Karpenter automatically configures the disks through an additional boostrap argument (`--local-disks raid0`). The device name is `/dev/md/0` and its mount point is `/mnt/k8s-disks/0`. You should ensure any additional disk setup does not interfere with these.

#### AL2023

On AL2023, Karpenter automatically configures the disks via the generated `NodeConfig` object. Like AL2, the device name is `/dev/md/0` and its mount point is `/mnt/k8s-disks/0`. You should ensure any additional disk setup does not interfere with these.

#### Bottlerocket

On Bottlerocket, Karpenter automatically configures the disks by adding the required bootstrap commands to the settings. The ephemeral storage configuration is handled automatically, so you should not manually configure ephemeral storage settings in your userData.



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    This automatic disk configuration is only available on Bottlerocket v1.22.0 and later.

</div>


#### Others

For all other AMI families, you must configure the disks yourself. Check out the [`setup-local-disks`](https://github.com/awslabs/amazon-eks-ami/blob/main/templates/shared/runtime/bin/setup-local-disks) script in [amazon-eks-ami](https://github.com/awslabs/amazon-eks-ami) to see how this is done for AL2.



<div class="alert alert-secondary" role="alert">
<h4 class="alert-heading">Tip</h4>

    Since the Kubelet &amp; Containerd will be using the instance-store filesystem, you may consider using a more minimal root volume size.

</div>


## spec.userData

You can control the UserData that is applied to your worker nodes via this field. This allows you to run custom scripts or pass-through custom configuration to Karpenter instances on start-up.

```yaml
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: bottlerocket-example
spec:
  ...
  amiFamily: Bottlerocket
  userData:  |
    [settings.kubernetes]
    "kube-api-qps" = 30
    "shutdown-grace-period" = "30s"
    "shutdown-grace-period-for-critical-pods" = "30s"
    [settings.kubernetes.eviction-hard]
    "memory.available" = "20%"
```

This example adds SSH keys to allow remote login to the node (replace *my-authorized_keys* with your public key file):



<div class="alert alert-primary" role="alert">
<h4 class="alert-heading">Note</h4>

    <p>Instead of using SSH as set up in this example, you can use Session Manager (SSM) or EC2 Instance Connect to gain shell access to Karpenter nodes.
See <a href="/preview/troubleshooting/index.md#node-notready">Node NotReady</a> troubleshooting for an example of starting an SSM session from the command line or <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-connect-set-up.html">EC2 Instance Connect</a> documentation to connect to nodes using SSH.</p>
<p>Also, <strong>my-authorized_key</strong> key is the public key. See <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/describe-keys.html#retrieving-the-public-key">Retrieve the public key material</a>.</p>


</div>


```yaml
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: al2-example
spec:
  ...
  amiFamily: AL2
  userData: |
    #!/bin/bash
    mkdir -p ~ec2-user/.ssh/
    touch ~ec2-user/.ssh/authorized_keys
    cat >> ~ec2-user/.ssh/authorized_keys <<EOF
    {{ insertFile "../my-authorized_keys" | indent 4  }}
    EOF
    chmod -R go-w ~ec2-user/.ssh/authorized_keys
    chown -R ec2-user ~ec2-user/.ssh
```

Alternatively, you can save the [key in your SSM Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-create-console.html) and use the get-parameter command mentioned below to retrieve the key for authorized_keys.

```
aws ssm get-parameter --name "<parameter-name>" --region <region> --with-decryption --query "Parameter.Value" --output text > /home/ec2-user/.ssh/authorized_keys
```

For more examples on configuring fields for different AMI families, see the [examples here](https://github.com/aws/karpenter/blob/main/examples/v1).

Karpenter will merge the userData you specify with the default userData for that AMIFamily. See the [AMIFamily](#specamifamily) section for more details on these defaults. View the sections below to understand the different merge strategies for each AMIFamily.

### AL2

* Your UserData can be in the [MIME multi part archive](https://cloudinit.readthedocs.io/en/latest/topics/format.html#mime-multi-part-archive) format.
* Karpenter will transform your custom user-data as a MIME part, if necessary, and then merge a final MIME part to the end of your UserData parts which will bootstrap the worker node. Karpenter will have full control over all the parameters being passed to the bootstrap script.
  * Karpenter will continue to set MaxPods, ClusterDNS and all other parameters defined in `spec.kubeletConfiguration` as before.

Consider the following example to understand how your custom UserData will be merged -

#### Passed-in UserData (bash)

```bash
#!/bin/bash
echo "Running custom user data script (bash)"
```

#### Merged UserData (bash)

```bash
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Running custom user data script (bash)"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
/etc/eks/bootstrap.sh 'test-cluster' --apiserver-endpoint 'https://test-cluster' --b64-cluster-ca 'ca-bundle' \
--use-max-pods false \
--kubelet-extra-args '--node-labels=karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test  --max-pods=110'
--//--
```

#### Passed-in UserData (MIME)

```bash
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"

--BOUNDARY
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Running custom user data script (mime)"

--BOUNDARY--
```

#### Merged UserData (MIME)

```bash
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Running custom user data script (mime)"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash -xe
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
/etc/eks/bootstrap.sh 'test-cluster' --apiserver-endpoint 'https://test-cluster' --b64-cluster-ca 'ca-bundle' \
--use-max-pods false \
--kubelet-extra-args '--node-labels=karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=test  --max-pods=110'
--//--
```



<div class="alert alert-secondary" role="alert">
<h4 class="alert-heading">Tip</h4>

    <p>You can set additional kubelet configuration properties, unavailable through <code>spec.kubelet</code>, by updating the <code>kubelet-config.json</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">karpenter.k8s.aws/v1</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EC2NodeClass</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">metadata</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">kubelet-config-example</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">amiFamily</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">AL2</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">userData</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">|</span><span style="color:#8f5902;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">    #!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">    echo &#34;$(jq &#39;.kubeAPIQPS=50&#39; /etc/kubernetes/kubelet/kubelet-config.json)&#34; &gt; /etc/kubernetes/kubelet/kubelet-config.json</span><span style="color:#f8f8f8;text-decoration:underline">    
</span></span></span></code></pre></div>

</div>


### AL2023

* Your UserData may be in one of three formats: a [MIME multi part archive](https://cloudinit.readthedocs.io/en/latest/topics/format.html#mime-multi-part-archive), a NodeConfig YAML / JSON string, or a shell script.
* Karpenter will transform your custom UserData into a MIME part, if necessary, and then create a MIME multi-part archive. This archive will consist of a generated NodeConfig, containing Karpenter's default values, followed by the transformed custom UserData. For more information on the NodeConfig spec, refer to the [AL2023 EKS Optimized AMI docs](https://awslabs.github.io/amazon-eks-ami/nodeadm/doc/examples/).



<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>

    Any values configured by the Karpenter generated NodeConfig object will take precedent over values specifed in <code>spec.userData</code>.
This includes cluster name, cluster CIDR, cluster endpoint, certificate authority, taints, labels, and any value in <a href="#speckubelet">spec.kubelet</a>.
These fields must be configured natively through Karpenter rather than through UserData.

</div>


#### Passed-in UserData (NodeConfig)

```yaml
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  kubelet:
    config:
      maxPods: 42
```

#### Merged UserData (NodeConfig)

```text
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: application/node.eks.aws

apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  kubelet:
    config:
      maxPods: 42

--//
Content-Type: application/node.eks.aws

# Karpenter Generated NodeConfig
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    apiServerEndpoint: https://test-cluster
    certificateAuthority: cluster-ca
    cidr: 10.100.0.0/16
    name: test-cluster
  kubelet:
    config:
      clusterDNS:
      - 10.100.0.10
      maxPods: 118
    flags:
    - --node-labels="karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=default"

--//--
```

#### Passed-in UserData (bash)

```shell
#!/bin/bash
echo "Hello, AL2023!"
```

#### Merged UserData (bash)

```text
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Hello, AL2023!"

--//
Content-Type: application/node.eks.aws

# Karpenter Generated NodeConfig
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    apiServerEndpoint: https://test-cluster
    certificateAuthority: cluster-ca
    cidr: 10.100.0.0/16
    name: test-cluster
  kubelet:
    config:
      clusterDNS:
      - 10.100.0.10
      maxPods: 118
    flags:
    - --node-labels="karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=default"

--//--
```

#### Passed-in UserData (MIME)

```text
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Hello, AL2023!"

--//
Content-Type: application/node.eks.aws

apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  kubelet:
    config:
      maxPods: 42
--//
```

#### Merged UserData (MIME)

```text
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: application/node.eks.aws

apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  kubelet:
    config:
      maxPods: 42
--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Hello, AL2023!"

--//
Content-Type: application/node.eks.aws

# Karpenter Generated NodeConfig
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    apiServerEndpoint: https://test-cluster
    certificateAuthority: cluster-ca
    cidr: 10.100.0.0/16
    name: test-cluster
  kubelet:
    config:
      clusterDNS:
      - 10.100.0.10
      maxPods: 118
    flags:
    - --node-labels="karpenter.sh/capacity-type=on-demand,karpenter.sh/nodepool=default"

--//--
```

### Bottlerocket

* Your UserData must be valid TOML.
* Unknown TOML fields will be ignored when the final merged UserData is generated by Karpenter.



<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>

    Any values configured by Karpenter will take precedent over values specifed in <code>spec.userData</code>.
This includes cluster name, cluster endpoint, cluster certificate, taints, labels, and any value in <a href="#speckubelet">spec.kubelet</a>.
These fields must be configured natively through Karpenter rather than through UserData.

</div>


Consider the following example to understand how your custom UserData settings will be merged in.

#### Passed-in UserData

```toml
[settings.kubernetes.eviction-hard]
"memory.available" = "12%"
[settings.kubernetes]
"unknown-setting" = "unknown"
[settings.kubernetes.node-labels]
'field.controlled.by/karpenter' = 'will-be-overridden'
```

#### Merged UserData

```toml
[settings]
[settings.kubernetes]
api-server = 'https://cluster'
cluster-certificate = 'ca-bundle'
cluster-name = 'cluster'

[settings.kubernetes.node-labels]
'karpenter.sh/capacity-type' = 'on-demand'
'karpenter.sh/nodepool' = 'default'

[settings.kubernetes.node-taints]

[settings.kubernetes.eviction-hard]
'memory.available' = '12%%'
```

#### Device ownership in Bottlerocket

Bottlerocket `v1.30.0+` supports device ownership using the [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) provided in the Kubernetes specfile. To enable this, you will need the following user-data configurations:

```toml
[settings]
[settings.kubernetes]
device-ownership-from-security-context = true
```

This allows the container to take ownership of devices allocated to the pod via device-plugins based on the `runAsUser` and `runAsGroup` values provided in the spec. For more details on this, see the [Kubernetes documentation](https://kubernetes.io/blog/2021/11/09/non-root-containers-and-devices/)

This setting helps you enable Neuron workloads on Bottlerocket instances. See [Accelerators/GPU Resources](/preview/concepts/scheduling/index.md#acceleratorsgpu-resources) for more details.

### Windows2019/Windows2022/Windows2025

* Your UserData must be specified as PowerShell commands.
* The UserData specified will be prepended to a Karpenter managed section that will bootstrap the kubelet.
* Karpenter will continue to set ClusterDNS and all other parameters defined in spec.kubeletConfiguration as before.

Consider the following example to understand how your custom UserData settings will be merged in.

#### Passed-in UserData

```powershell
Write-Host "Running custom user data script"
```

#### Merged UserData

```powershell
<powershell>
Write-Host "Running custom user data script"
[string]$EKSBootstrapScriptFile = "$env:ProgramFiles\Amazon\EKS\Start-EKSBootstrap.ps1"
& $EKSBootstrapScriptFile -EKSClusterName 'test-cluster' -APIServerEndpoint 'https://test-cluster' -Base64ClusterCA 'ca-bundle' -KubeletExtraArgs '--node-labels="karpenter.sh/capacity-type=spot,karpenter.sh/nodepool=windows2022" --max-pods=110' -DNSClusterIP '10.0.100.10'
</powershell>
```



<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Windows Support Notice</h4>

    <p>Currently, Karpenter does not specify <code>-ServiceCIDR</code> to <a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-windows-ami.html#bootstrap-script-configuration-parameters">EKS Windows AMI Bootstrap script</a>.
Windows worker nodes will use <code>172.20.0.0/16</code> or <code>10.100.0.0/16</code> for Kubernetes service IP address ranges based on the IP address of the primary interface.
The effective ServiceCIDR can be verified at <code>$env:ProgramData\Amazon\EKS\cni\config\vpc-bridge.conf</code> on the worker node.</p>
<p>Support for the Windows ServiceCIDR argument can be tracked in a <a href="https://github.com/aws/karpenter/issues/4088">Karpenter Github Issue</a>. Currently, if the effective ServiceCIDR is incorrect for your windows worker nodes, you can add the following userData as a workaround.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline">  </span><span style="color:#204a87;font-weight:bold">userData</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">|</span><span style="color:#8f5902;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">    $global:EKSCluster = Get-EKSCluster -Name my-cluster</span><span style="color:#f8f8f8;text-decoration:underline">    
</span></span></span></code></pre></div>

</div>


### Custom

* No merging is performed, your UserData must perform all setup required of the node to allow it to join the cluster.
* Custom UserData must meet the following requirements to work correctly with Karpenter:
  * It must ensure the node is registered with the `karpenter.sh/unregistered:NoExecute` taint (via kubelet configuration field `registerWithTaints`)
  * It must set kubelet config options to match those configured in `spec.kubelet`

## spec.detailedMonitoring

Enabling detailed monitoring controls the [EC2 detailed monitoring](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-cloudwatch-new.html) feature. If you enable this option, the Amazon EC2 console displays monitoring graphs with a 1-minute period for the instances that Karpenter launches.

```yaml
spec:
  detailedMonitoring: true
```

## spec.associatePublicIPAddress

You can explicitly set `AssociatePublicIPAddress: false` when you are only launching into private subnets.
Previously, Karpenter auto-set `associatePublicIPAddress` on the primary ENI to false if a user’s subnet options were all private subnets.
This value is a boolean field that controls whether instances created by Karpenter for this EC2NodeClass will have an associated public IP address. This overrides the `MapPublicIpOnLaunch` setting applied to the subnet the node is launched in. If this field is not set, the `MapPublicIpOnLaunch` field will be respected.




<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Note</h4>

    If a <code>NodeClaim</code> requests <code>vpc.amazonaws.com/efa</code> resources, <code>spec.associatePublicIPAddress</code> is respected. However, if this <code>NodeClaim</code> requests <strong>multiple</strong> EFA resources and the value for <code>spec.associatePublicIPAddress</code> is true, the instance will fail to launch. This is due to an EC2 restriction which
requires that the field is only set to true when configuring an instance with a single ENI at launch. When using this field, it is advised that users segregate their EFA workload to use a separate <code>NodePool</code> / <code>EC2NodeClass</code> pair.

</div>


## spec.ipPrefixCount

This value is a integer field that controls how many ip prefixes will be assigned to `NodeClaim`. See the [EC2 Launch Template Network Interface Spec](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ec2-launchtemplate-networkinterface.html) for more information. Sets ipv4PrefixCount if you are using an IPv4 Cluster, or ipv6PrefixCount if you are using IPv6.

## status.subnets
[`status.subnets`](#statussubnets) contains the resolved `id` and `zone` of the subnets that were selected by the [`spec.subnetSelectorTerms`](#specsubnetselectorterms) for the node class. The subnets will be sorted by the available IP address count in decreasing order.

#### Examples

```yaml
spec:
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}"
status:
  subnets:
  - id: subnet-0a462d98193ff9fac
    zone: us-east-2b
  - id: subnet-0322dfafd76a609b6
    zone: us-east-2c
  - id: subnet-0727ef01daf4ac9fe
    zone: us-east-2b
  - id: subnet-00c99aeafe2a70304
    zone: us-east-2a
  - id: subnet-023b232fd5eb0028e
    zone: us-east-2c
  - id: subnet-03941e7ad6afeaa72
    zone: us-east-2a
```

## status.securityGroups

[`status.securityGroups`](#statussecuritygroups) contains the resolved `id` and `name` of the security groups that were selected by the [`spec.securityGroupSelectorTerms`](#specsecuritygroupselectorterms) for the node class. The subnets will be sorted by the available IP address count in decreasing order.

#### Examples

```yaml
spec:
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}"
status:
  securityGroups:
  - id: sg-041513b454818610b
    name: ClusterSharedNodeSecurityGroup
  - id: sg-0286715698b894bca
    name: ControlPlaneSecurityGroup-1AQ073TSAAPW
```

## status.amis

[`status.amis`](#statusamis) contains the resolved `id`, `name`, `requirements`, and the `deprecated` status of either the default AMIs for the [`spec.amiFamily`](#specamifamily) or the AMIs selected by the [`spec.amiSelectorTerms`](#specamiselectorterms) if this field is specified. The `deprecated` status will be shown for resolved AMIs that are deprecated.

#### Examples

AMIs resolved with an AL2 alias:

```yaml
spec:
  amiSelectorTerms:
    - alias: al2@v20240807
status:
  amis:
  - id: ami-03c3a3dcda64f5b75
    name: amazon-linux-2-gpu
    requirements:
    - key: kubernetes.io/arch
      operator: In
      values:
      - amd64
    - key: karpenter.k8s.aws/instance-gpu-count
      operator: Exists
  - id: ami-03c3a3dcda64f5b75
    name: amazon-linux-2-gpu
    requirements:
    - key: kubernetes.io/arch
      operator: In
      values:
      - amd64
    - key: karpenter.k8s.aws/instance-accelerator-count
      operator: Exists
  - id: ami-06afb2d101cc4b8bd
    name: amazon-linux-2-arm64
    requirements:
    - key: kubernetes.io/arch
      operator: In
      values:
      - arm64
    - key: karpenter.k8s.aws/instance-gpu-count
      operator: DoesNotExist
    - key: karpenter.k8s.aws/instance-accelerator-count
      operator: DoesNotExist
  - id: ami-0e28b76d768af234e
    name: amazon-linux-2
    requirements:
    - key: kubernetes.io/arch
      operator: In
      values:
      - amd64
    - key: karpenter.k8s.aws/instance-gpu-count
      operator: DoesNotExist
    - key: karpenter.k8s.aws/instance-accelerator-count
      operator: DoesNotExist
```

AMIs resolved from tags:

```yaml
spec:
  amiSelectorTerms:
    - tags:
        karpenter.sh/discovery: "${CLUSTER_NAME}"
status:
  amis:
  - id: ami-01234567890123456
    name: custom-ami-amd64
    deprecated: true
    requirements:
    - key: kubernetes.io/arch
      operator: In
      values:
      - amd64
  - id: ami-01234567890123456
    name: custom-ami-arm64
    requirements:
    - key: kubernetes.io/arch
      operator: In
      values:
      - arm64
```

## status.capacityReservations

[`status.capacityReservations`](#statuscapacityreservations) contains the following information for each resolved capacity reservation:

| Field                   | Example                | Description                                                                          |
| ----------------------- | ---------------------- | ------------------------------------------------------------------------------------ |
| `availabilityZone`      | `us-east-1a`           | The availability zone the capacity reservation is available in                       |
| `id`                    | `cr-56fac701cc1951b03` | The ID of the capacity reservation                                                   |
| `instanceMatchCriteria` | `open`                 | The instanceMatchCriteria for the capacity reservation. Can be `open` or `targeted`. |
| `instanceType`          | `m5.large`             | The EC2 instance type of the capacity reservation                                    |
| `ownerID`               | `459763720645`         | The account ID that owns the capacity reservation                                    |
| `reservationType`       | `default`              | The type of the capacity reservation. Can be `default` or `capacity-block`.          |
| `state`                 | `active`               | The state of the capacity reservation. Can be `active` or `expiring`.                |
| `interruptible`         | `true` or `false`      | Whether the capacity reservation is interruptible.                                   |

#### Examples

```yaml
status:
  capacityReservations:
  - availabilityZone: us-west-2a
    id: cr-01234567890123456
    instanceMatchCriteria: targeted
    instanceType: g6.48xlarge
    interruptible: false
    ownerID: "012345678901"
    reservationType: capacity-block
    state: expiring
  - availabilityZone: us-west-2c
    id: cr-12345678901234567
    instanceMatchCriteria: open
    instanceType: g6.48xlarge
    interruptible: true
    ownerID: "98765432109"
    reservationType: default
    state: active
```

## status.instanceProfile

[`status.instanceProfile`](#statusinstanceprofile) contains the resolved instance profile generated by Karpenter from the [`spec.role`](#specrole)

```yaml
spec:
  role: "KarpenterNodeRole-${CLUSTER_NAME}"
status:
  instanceProfile: "${CLUSTER_NAME}-0123456778901234567789"
```

## status.conditions

[`status.conditions`](#statusconditions) indicates EC2NodeClass readiness. This will be `Ready` when Karpenter successfully discovers AMIs, Instance Profile, Subnets, Cluster CIDR (AL2023 only) and SecurityGroups for the EC2NodeClass.

NodeClasses have the following status conditions:

| Condition Type       | Description                                                                                                                                                                                                                       |
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SubnetsReady         | Subnets are discovered.                                                                                                                                                                                                           |
| SecurityGroupsReady  | Security Groups are discovered.                                                                                                                                                                                                   |
| InstanceProfileReady | Instance Profile is discovered.                                                                                                                                                                                                   |
| AMIsReady            | AMIs are discovered.                                                                                                                                                                                                              |
| Ready                | Top level condition that indicates if the nodeClass is ready. If any of the underlying conditions is `False` then this condition is set to `False` and `Message` on the condition indicates the dependency that was not resolved. |

If a NodeClass is not ready, NodePools that reference it through their `nodeClassRef` will not be considered for scheduling.

