---
title: Configure AKSNodeClass Resources for Node Auto-Provisioning (NAP) in Azure Kubernetes Service (AKS)
description: Learn how to configure Azure-specific settings for AKS node auto-provisioning using AKSNodeClass resources.
ms.topic: how-to
ms.custom: devx-track-azurecli, aks-scaling
ms.date: 07/5/2026
ms.author: schaffererin
author: schaffererin
ms.service: azure-kubernetes-service
# Customer intent: As a cluster operator or developer, I want to configure Azure-specific settings for AKS node auto-provisioning using AKSNodeClass resources, so that I can optimize node provisioning for my workloads while leveraging Azure's capabilities.
---

# Configure AKSNodeClass resources for node auto-provisioning (NAP) in Azure Kubernetes Service (AKS)

This article explains how to configure `AKSNodeClass` resources to define Azure-specific settings for node auto-provisioning (NAP) in Azure Kubernetes Service (AKS) using Karpenter. `AKSNodeClass` allows you to customize various aspects of the nodes that Karpenter provisions, such as the virtual machine (VM) image, operating system (OS) disk size, maximum pods per node, and kubelet configurations.

[!INCLUDE [azure linux 2.0 retirement](./includes/azure-linux-retirement.md)]

## Overview of AKSNodeClass resources

`AKSNodeClass` resources enable you to configure Azure-specific settings for NAP. Each [`NodePool` resource](./node-auto-provisioning-node-pools.md) must reference an `AKSNodeClass` using `spec.template.spec.nodeClassRef`. You can have multiple `NodePools` that point to the same `AKSNodeClass`, allowing you to share common Azure configurations across different node pools.

## Image family configuration

The `imageFamily` field sets the default VM image and bootstrapping logic for nodes provisioned through the `AKSNodeClass`. If you don't specify an image family, the default OS version according to your Kubernetes version is used. GPUs are supported with both image families on compatible VM sizes. For more information about the default OS version per Kubernetes version, see the [AKS OS Version documentation](./upgrade-os-version.md).

### Supported image families

- **`Ubuntu`**: Ubuntu is the default Linux distribution for AKS nodes.
  - OS version defaults change based on your Kubernetes version. Ubuntu 22.04 is default for Kubernetes versions 1.25 to 1.33. Ubuntu 24.04 is default for Kubernetes versions 1.34 and later.
- **`AzureLinux`**: Azure Linux is Microsoft's alternative Linux distribution for AKS workloads. For more information, see the [Azure Linux documentation](/azure/aks/use-azure-linux)

#### Example image family configuration

The following example configures the `AKSNodeClass` to use the `AzureLinux` image family:

```yaml
spec:
  imageFamily: AzureLinux
```

#### FIPS compliant node image configuration
You can also enable Federal Information Processing Standards (FIPS) compliant node images. For more information about FIPS in AKS, see [FIPS documentation](./enable-fips-nodes.md).

The `fipsMode` field is set by default to Disabled, and can be set to the following options:
- FIPS - select FIPS-compliant node images
- Disabled - do not use FIPS-compliant node images

The following example configures the 'AKSNodeClass' to select FIPS-compliant node images by setting `fipsMode` to `FIPS`:

```yaml
spec:
  fipsMode: FIPS
```

## Virtual network (VNet) subnet configuration

The `vnetSubnetID` field specifies which Azure VNet subnet should be used for provisioning node network interfaces. This field is optional. If you don't specify a subnet, NAP uses the default subnet configured during Karpenter installation. For more information, see [Subnet configurations for NAP](./node-auto-provisioning-networking.md#subnet-configurations-for-nap).

### Example subnet configuration

The subnet ID must be in the full Azure Resource Manager (ARM) format, as shown in the following example:

```yaml
spec:
  vnetSubnetID: "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Network/virtualNetworks/{vnet-name}/subnets/{subnet-name}"
```

## OS disk size configuration

The `osDiskSizeGB` field specifies the size of the OS disk in gigabytes. The default value is 128 GB, and the minimum value is 30 GB.

Consider larger OS disk sizes for workloads that:

- Store significant data locally.
- Require extra space for container images.
- Have high disk I/O requirements.

### Example OS disk size configuration

```yaml
spec:
  osDiskSizeGB: 256  # 256 GB OS disk
```

## Ephemeral OS disk configuration

NAP automatically uses [Ephemeral OS disks](/azure/virtual-machines/ephemeral-os-disks) when available and suitable for the requested disk size. Ephemeral OS disks provide better performance and lower cost compared to managed disks.

### Ephemeral disk selection criteria

The system automatically chooses Ephemeral disks in the following scenarios:

- The VM instance type supports Ephemeral OS disks.
- The Ephemeral disk capacity is greater than or equal to the requested `osDiskSizeGB`.
- The VM has sufficient ephemeral storage capacity.

If these conditions aren't met, the system falls back to using managed disks.

### Ephemeral disk types and prioritization

Azure VMs can have different types of ephemeral storage. The system uses the following priority order:

- **NVMe disks** (highest performance)
- **Cache disks** (balanced performance)
- **Resource disks** (basic performance)

### Example ephemeral disk configuration

You can use node pool requirements to ensure nodes have sufficient ephemeral disk capacity, as shown in the following example:

```yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: ephemeral-disk-pool
spec:
  template:
    spec:
      requirements:
        - key: karpenter.azure.com/sku-storage-ephemeralos-maxsize
          operator: Gt
          values: ["128"]  # Require ephemeral disk larger than 128 GB
      nodeClassRef:
        group: karpenter.azure.com
        kind: AKSNodeClass
        name: my-node-class
---
apiVersion: karpenter.azure.com/v1beta1
kind: AKSNodeClass
metadata:
  name: my-node-class
spec:
  osDiskSizeGB: 128  # This will use ephemeral disk if available and large enough
```

This configuration ensures that only VM instance types with ephemeral disks larger than 128 GB are selected, guaranteeing ephemeral disk usage for the specified OS disk size.

## Maximum pods configuration

The `maxPods` field specifies the maximum number of pods that can be scheduled on a node. This setting affects both cluster density and network configuration.

The minimum value for `maxPods` is 10, and the maximum value is 250.

### Default behavior for `maxPods`

The default behavior for `maxPods` depends on the network plugin configuration. The following table summarizes the defaults:

| Network plugin configuration | Default `maxPods` per node |
|------------------------------|----------------------------|
| Azure CNI with standard networking (v1 or NodeSubnet) | 30 |
| Azure CNI with overlay networking | 250 |
| None (no network plugin) | 250 |
| Other configurations | 110 (standard Kubernetes default) |

### Example maximum pods configuration

```yaml
spec:
  maxPods: 50  # Allow up to 50 pods per node
```

## LocalDNS configuration

LocalDNS deploys a node level DNS proxy that resolves DNS queries closer to workloads, reducing query latency and improving resiliency during transient DNS disruptions. For more information, see the [LocalDNS documentation](./localdns-custom.md). By default, LocalDNS is set to Disabled and can be configured to the following options:

- `Disabled` (default) - Disables the LocalDNS feature. DNS queries aren't resolved locally on the node.
- `Preferred` - AKS manages LocalDNS enablement based on the Kubernetes version of the node pool. The configuration is always validated and included, but LocalDNS isn't enabled unless the correct Kubernetes version is used.
- `Required` - LocalDNS is enforced on the node pool if all prerequisites are satisfied. If the requirements aren't met, the deployment fails.

### Example LocalDNS configuration

You can customize LocalDNS configurations such as `vnetDNSOverrides` and `kubeDNSOverrides`. For more details on the supported plugins, see [Customize LocalDNS](./localdns-custom.md).

```yaml
spec:
  LocalDNS:
    mode: Required
    vnetDNSOverrides:
      - zone: "."
        cacheDuration: "3600s"
        forwardDestination: VnetDNS
        forwardPolicy: Sequential
        maxConcurrent: 1000
        protocol: PreferUDP
        queryLogging: Error
        serveStale: Immediate
        serveStaleDuration: "3600s"
      - zone: "cluster.local"
        cacheDuration: "3600s"
        forwardDestination: ClusterCoreDNS
        forwardPolicy: Sequential
        maxConcurrent: 1000
        protocol: ForceTCP
        queryLogging: Error
        serveStale: Immediate
        serveStaleDuration: "3600s"
    kubeDNSOverrides:
      - zone: "."
        cacheDuration: "3600s"
        forwardDestination: ClusterCoreDNS
        forwardPolicy: Sequential
        maxConcurrent: 1000
        protocol: PreferUDP
        queryLogging: Error
        serveStale: Immediate
        serveStaleDuration: "3600s"
      - zone: "cluster.local"
        cacheDuration: "3600s"
        forwardDestination: ClusterCoreDNS
        forwardPolicy: Sequential
        maxConcurrent: 1000
        protocol: ForceTCP
        queryLogging: Error
        serveStale: Immediate
        serveStaleDuration: "3600s"
``` 

## Kubelet configuration

The `kubelet` section allows you to configure various kubelet parameters that affect node behavior. These parameters are typical kubelet arguments, so the Azure provider simply passes them through to the kubelet on the node.

> [!IMPORTANT]
> **Configure kubelet settings carefully**, and test any changes in nonproduction environments first.

### CPU management

The following settings control CPU management behavior for the kubelet:

```yaml
spec:
  kubelet:
    cpuManagerPolicy: "static"  # or "none"
    cpuCFSQuota: true
    cpuCFSQuotaPeriod: "100ms"
```

- `cpuManagerPolicy`: Controls how the kubelet allocates CPU resources. Set to `"static"` for CPU pinning in latency-sensitive workloads.
- `cpuCFSQuota`: Enables CPU Completely Fair Scheduler (CFS) quota enforcement for containers that specify CPU limits.
- `cpuCFSQuotaPeriod`: Sets the CPU CFS quota period.

### Image garbage collection

The following settings control image garbage collection behavior for the kubelet:

```yaml
spec:
  kubelet:
    imageGCHighThresholdPercent: 85
    imageGCLowThresholdPercent: 80
```

These settings control when the kubelet performs garbage collection of container images:

- `imageGCHighThresholdPercent`: Disk usage percentage that triggers image garbage collection.
- `imageGCLowThresholdPercent`: Target disk usage percentage after garbage collection.

### Topology management

The following setting controls the topology manager policy for the kubelet:

```yaml
spec:
  kubelet:
    topologyManagerPolicy: "best-effort"  # none, restricted, best-effort, single-numa-node
```

The topology manager helps coordinate resource allocation for latency-sensitive workloads across CPU and device (like GPU) resources.

### System configuration

The following settings allow you to configure extra system parameters for the kubelet:

```yaml
spec:
  kubelet:
    allowedUnsafeSysctls:
      - "kernel.msg*"
      - "net.ipv4.route.min_pmtu"
    containerLogMaxSize: "50Mi"
    containerLogMaxFiles: 5
    podPidsLimit: 4096
```

- `allowedUnsafeSysctls`: List of permitted unsafe sysctls that pods can use.
- `containerLogMaxSize`: Maximum size of container log files before rotation.
- `containerLogMaxFiles`: Maximum number of container log files to retain.
- `podPidsLimit`: Maximum number of processes allowed in any pod.

## Linux custom OS configuration settings

The `LinuxOSConfig` section allows you to configure various kubelet parameters that affect node behavior. These parameters are typical custom OS arguments, so the NAP simply passes them through to the kubelet on the node.

For more on custom Linux OS configuration settings, full details on default values, and considerations, see our [custom node configuration documentation](./custom-node-configuration.md#linux-custom-os-configuration-settings).

> [!IMPORTANT]
> **Configure Linux OS settings carefully**, and test any changes in nonproduction environments first.

###  Linux file handle limits settings

Use the following settings to set the file system settings.

```yaml 
spec:
  linuxOSConfig: 
    # Sysctl Settings  
    sysctls:  
      # File System Settings  
      fsFileMax: 2000000               # Range: 8192-12000500  Default: Max of available range
      fsInotifyMaxUserWatches: 1000000 # Range: 781250-2097152 Default: 1048576
      fsAioMaxNr: 1000000              # Range: 65536-6553500  Default: 65536
      fsNrOpen: 1000000                # Range: 8192-20000500  Default: 1048576
```

### Linux socket and network tuning settings

Use the following settings to set the TCP and network setting.

```yaml 
spec:
  linuxOSConfig:
      # Network Settings  
      netCoreSomaxconn: 65535          # Range: 4096-3240000  Default: 16384
      netCoreNetdevMaxBacklog: 5000    # Range: 1000-3240000  Default: 1000
      netCoreRmemMax: 134217728        # Range: 212992-134217728  Default: 1048576
      netCoreOptmemMax: 102400         # Range: 20480-4194304     Default: 131072 
      netCoreWmemMax: 134217728        # Range: 212992-134217728  Default: 212992
      netCoreRmemDefault: 212992       # Range: 212992-134217728  
      netCoreWmemDefault: 212992       # Range: 212992-134217728  
      netIPv4IPLocalPortRange: "1024 65535" # Format: "first last", first: 1024-60999, last: 32768-65535  

      # Neighbor Table GC Thresholds  
      netIPv4NeighDefaultGcThresh1: 1024   # Range: 128-80000  Default: 4096
      netIPv4NeighDefaultGcThresh2: 2048   # Range: 512-80000  Default: 8192
      netIPv4NeighDefaultGcThresh3: 4096   # Range: 1024-80000  Default: 16384
      # Note: thresh1 <= thresh2 <= thresh3  

      # Connection Tracking  
      netNetfilterNfConntrackBuckets: 131072  # Range: 65536-524288   Default: dynamically calculated 
      netNetfilterNfConntrackMax: 262144       # Range: 131072-2097152 Default: dynamically calculated 
```

### Linux worker limit settings

Use the following settings to set the kernel setting.

```yaml 
spec:
  linuxOSConfig:
      # Kernel Settings  
      kernelThreadsMax: 100000         # Range: 20-513785  Default: Dynamically calculated
```


### Linux virtual memory settings

Use the following options to tune the operation of the Linux virtual memory subsystem of the Linux kernel and the writeout of dirty data to disk.

```yaml 
spec:
  linuxOSConfig:
      # Memory Management  
      vmMaxMapCount: 262144          # Range: 65530-262144 Default: Max of available range
      vmVfsCachePressure: 100        # Range: 0-100  Default: 100
      vmSwappiness: 60               # Range: 0-100  Default: 60

      # Swap File Configuration  
      swapFileSize: "2Gi"             # Default: (not set) | Pattern: quantity with units Note: Requires kubelet.failSwapOn: false  

      # Transparent Huge Pages  
      transparentHugePageEnabled: "madvise"    # Values: [always, madvise, never]  Default: always
      transparentHugePageDefrag: "defer+madvise" # Values: [always, defer, defer+madvise, madvise, never]  Default: madvise
```

## GPU settings

The following field allows user to allow custom GPU driver installation, such as with NVIDIA GPU Operator. 

```yaml 
spec:
  gpu:
    mode: 
      # acceptable values: [driver, none] default(or if not specified): driver
      # none skips gpu driver installation, driver has NAP manage the GPU driver installation  
      none
```

## Security settings

### Encryption at host

The following field specifies whether host-level encryption is enabled for provisioned nodes. When you set this field to `true`, NAP includes only instance options that support encryption at host.

```yaml 
spec:
  security:
    encryptionatHost: 
      # acceptable values: [true, false] default(or if not specified): false  
      false
```

For more information about host-based encryption, see [Encryption at Host documentation](./enable-host-encryption.md).

### Custom-managed keys and disk encryption sets

NAP supports clusters that use customer-managed keys and disk encryption sets. You enable these options at the cluster level. They don't have AKSNodeClass fields that you need to set. Make sure that your cluster identity has the proper [role-based access control (RBAC)](./aks-desktop-permissions.md):

- The cluster identity has `Reader` access to the Disk Encryption Set.
- The Disk Encryption set resource has `Key Vault Crypto Service Encryption User` access to the Azure Key Vault. 

For information on customer-managed keys and disk encryption sets enabled during cluster creation, see [Customer-Managed Keys documentation](./azure-disk-customer-managed-keys.md).

## Azure resource tags configuration

You can specify Azure resource tags that apply to all VM instances created using a particular `AKSNodeClass` resource. Tags are useful for cost tracking, resource organization, and compliance requirements.

### Tag limitations

- Azure resource tags have a limit of 50 tags per resource.
- Tag names are case-insensitive but tag values are case-sensitive.
- Azure reserves some tag names that can't be used. For more information, see [Tag guidance and limits](/azure/azure-resource-manager/management/tag-resources#tag-restrictions).

### Example tags configuration

```yaml
spec:
  tags:
    Environment: "production"
    Team: "platform"
    Application: "web-service"
    CostCenter: "engineering"
```

## Comprehensive `AKSNodeClass` configuration example

The following example demonstrates a comprehensive `AKSNodeClass` configuration that includes all the settings discussed in this article:

```yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.azure.com
        kind: AKSNodeClass
        name: comprehensive-example
---
apiVersion: karpenter.azure.com/v1beta1
kind: AKSNodeClass
metadata:
  name: comprehensive-example
spec:
  # Image family configuration
  # Default: Ubuntu
  # Valid values: Ubuntu, AzureLinux, AzureContainerLinux
  imageFamily: Ubuntu

  # FIPS compliant mode - allows support for FIPS-compliant node images
  # Default: Disabled
  # Valid values: FIPS, Disabled
  fipsMode: Disabled

  # LocalDNS mode - allows use of LocalDNS feature
  # Default: Disabled
  # Valid values: Preferred, Required, Disabled
  LocalDNS:
    mode: Disabled
    # additional details on vnetDNSOverrides and kubeDNSOverrides can be added here

  # Virtual network subnet configuration (optional)
  # If not specified, uses the default --vnet-subnet-id from Karpenter installation
  vnetSubnetID: "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet"

  # OS disk size configuration
  # Default: 128 GB
  # Minimum: 30 GB
  osDiskSizeGB: 128

  # Maximum pods per node configuration
  # Default behavior depends on network plugin:
  # - Azure CNI with standard networking: 30 pods
  # - Azure CNI with overlay networking: 250 pods
  # - Other configurations: 110 pods
  # Range: 10-250
  maxPods: 30

  # GPU driver installation (optional)
  # Default: driver - NAP manages gpu driver installation
  # none skips gpu driver installation
  # Valid values: driver, none
  gpu:
    mode: 
      driver

  # Azure resource tags (optional)
  # Applied to all VM instances created with this AKSNodeClass
  tags:
    Environment: "production"
    Team: "platform-team"
    Application: "web-service"
    CostCenter: "engineering"

  # Kubelet configuration (optional)
  # All fields are optional with sensible defaults
  kubelet:
    # CPU management policy
    # Default: "none"
    # Valid values: none, static
    cpuManagerPolicy: "static"

    # CPU CFS quota enforcement
    # Default: true
    cpuCFSQuota: true

    # CPU CFS quota period
    # Default: "100ms"
    cpuCFSQuotaPeriod: "100ms"

    # Image garbage collection thresholds
    # imageGCHighThresholdPercent must be greater than imageGCLowThresholdPercent
    # Range: 0-100
    imageGCHighThresholdPercent: 85
    imageGCLowThresholdPercent: 80

    # Topology manager policy
    # Default: "none"
    # Valid values: none, restricted, best-effort, single-numa-node
    topologyManagerPolicy: "best-effort"

    # Allowed unsafe sysctls (optional)
    # Comma-separated list of unsafe sysctls or patterns
    allowedUnsafeSysctls:
      - "kernel.msg*"
      - "net.ipv4.route.min_pmtu"

    # Container log configuration
    # containerLogMaxSize default: "50Mi"
    containerLogMaxSize: "50Mi"
    
    # containerLogMaxFiles default: 5, minimum: 2
    containerLogMaxFiles: 5

    # Pod process limits
    # Default: -1 (unlimited)
    podPidsLimit: 4096
```

## Next steps

For more information on node auto-provisioning in AKS, see the following articles:

- [Use node auto-provisioning in a custom virtual network](./node-auto-provisioning-custom-vnet.md)
- [Configure node pools for node auto-provisioning on AKS](./node-auto-provisioning-node-pools.md)
- [Configure disruption policies for node auto-provisioning on AKS](./node-auto-provisioning-disruption.md)
