Edit

Configure LocalDNS in Azure Kubernetes Service (AKS)

Applies to: ✔️ AKS Automatic ✔️ AKS Standard

For most production workloads, AKS Automatic is the recommended production-ready default for AKS. LocalDNS is preconfigured in AKS Automatic clusters. In AKS Standard, you can enable and configure LocalDNS per node pool.

LocalDNS is a feature in AKS that improves DNS resolution performance and resiliency for workloads running in your cluster. By running a DNS proxy on each node, LocalDNS reduces DNS query latency, improves reliability during transient network disruptions, and provides advanced caching and forwarding controls when you need customization.

To learn what LocalDNS is, including architecture details and key capabilities, see DNS Resolution in Azure Kubernetes Service (AKS).

AKS Automatic and AKS Standard LocalDNS behavior

Behavior AKS Automatic AKS Standard
LocalDNS availability Preconfigured by default Optional
Typical action Validate and monitor defaults, customize only when required Enable, configure, and tune per node pool
Production guidance Recommended production-ready default for most AKS workloads Use when you need full manual control of cluster configuration

Best practices for LocalDNS configuration

When implementing LocalDNS in your AKS clusters, consider the following best practices:

  • Start with a minimal configuration: Begin with a simple configuration that uses the Preferred mode to validate your LocalDNS configuration syntax before moving to Required mode. The Preferred mode validates your configuration without enabling LocalDNS, allowing you to catch configuration errors early without impacting your cluster.
  • Implement proper caching strategies: Configure cache settings based on your workload characteristics:
    • For frequently changing records, use shorter cacheDurationInSeconds values. When doing so, it's important to note that cacheDurationInSeconds acts as a cap on the DNS record TTL but doesn't increase it. The resulting TTL is the smaller of what is returned from upstream or what is set in the cache plugin.
    • For stable records, use longer cache durations to reduce DNS queries.
    • Enable serveStale with appropriate settings to maintain service during DNS outages.
    • Caching with LocalDNS operates on a best effort basis and doesn't guarantee stale responses. The cache is divided into 256 shards and with a default maximum of 10,000 entries, allowing each shard to hold about 39 entries. When a shard is full and a new entry needs to be added, one of the existing entries is chosen at random to be evicted. There's no preference for older or expires entries. As a result, a stale record might not always be available, especially under high query volume.
  • Monitor DNS performance: After enabling LocalDNS, monitor your application's DNS performance using:
    • Application performance metrics.
    • Node metrics to detect reduced network pressure.
    • Log entries when queryLogging is set to Log.
  • Follow least privilege principle: When configuring DNS forwarding rules, only allow access to the required DNS servers and domains.
  • Test before production deployment: Always test LocalDNS configuration in a nonproduction environment before rolling it out to production clusters.
  • Use Infrastructure as Code (IaC): Store your localdnsconfig.json file in your infrastructure repository and include it in your AKS deployment templates.
  • Network configuration for TCP forwarding: When using TCP for DNS forwarding to VnetDNS, ensure that your Network Security Groups (NSGs), firewalls, or Network Virtual Appliances (NVAs) don't block TCP traffic between CoreDNS/LocalDNS and VnetDNS servers.
  • Avoid enabling both NodeLocal DNSCache and LocalDNS: It isn't recommended to enable both the upstream Kubernetes NodeLocal DNSCache and LocalDNS in your node pool. While AKS doesn't block this configuration, all DNS traffic is routed through LocalDNS, which might lead to unexpected behavior or reduced benefits from NodeLocal DNSCache.
  • Don't impose a TCP connection cap on the upstream custom DNS server before enabling LocalDNS: When you enable LocalDNS on a node pool, each node opens long-lived TCP connections from its local DNS proxy to the upstream resolver, instead of the short UDP exchanges used previously. If your custom DNS server (such as BIND, Unbound, Windows DNS, or a third-party appliance) is configured with a fixed limit on concurrent TCP client connections, or if you adjusted that limit based on pre-LocalDNS traffic, the new TCP connections from LocalDNS can be rejected, causing cluster-wide DNS resolution failures. Leave any TCP connection limit at a generous default before turning LocalDNS on, validate the steady-state TCP connection count from your AKS nodes after enablement, and only adjust the limit afterward with headroom for node scale-out, upgrades, and reimages.

Prerequisites

AKS Automatic clusters include LocalDNS preconfigured. The prerequisites in this section mainly apply when you enable or customize LocalDNS behavior, which is most common in AKS Standard and advanced customization scenarios.

  • You must have an existing AKS cluster with Kubernetes versions 1.31 and later to use LocalDNS. If you need an AKS cluster, you can create one using Azure CLI, Azure PowerShell, or the Azure portal.
  • This article requires Azure CLI version 2.80.0 and later. If you're using Azure Cloud Shell, the latest version is already installed.
  • LocalDNS is only supported on node pools running Azure Linux or Ubuntu 22.04 and newer.
  • The Virtual Machine (VM) SKU used for your node pool must have at least 4 vCPUs (cores) to support LocalDNS.
  • LocalDNS isn't compatible with applied Fully Qualified Domain Names (FQDN) filter policies in Advanced Container Networking Services (ACNS).

Enable or customize LocalDNS on an AKS cluster

You configure LocalDNS at the node pool level in AKS, so you can tailor behavior by workload and environment.

In AKS Automatic, LocalDNS is already preconfigured, so this section is primarily for customization.

In AKS Standard, use this section to enable and configure LocalDNS.

Enable LocalDNS on a node pool

Note

If you're using Node Auto-Provisioning (NAP), see LocalDNS configuration for instructions on how to enable LocalDNS with NAP.

This step typically applies to AKS Standard. AKS Automatic already includes LocalDNS preconfigured.

To enable LocalDNS during node pool creation, use the following command with your custom configuration file:

az aks nodepool add --name mynodepool1 --cluster-name myAKSCluster --resource-group myResourceGroup --localdns-config ./localdnsconfig.json

To enable LocalDNS on an existing node pool, use the following command with your custom configuration file:

az aks nodepool update --name mynodepool1 --cluster-name myAKSCluster --resource-group myResourceGroup --localdns-config ./localdnsconfig.json

Important

Enabling LocalDNS on a node pool initiates a reimage operation on all nodes within that pool. This process can cause temporary disruption to running workloads and might lead to application downtime if not properly managed. You should plan for potential service interruptions and ensure that the applications are configured for high availability or have appropriate disruption budgets in place before enabling this setting.

Disable LocalDNS on a node pool

Note

If you're using Node Auto-Provisioning (NAP), see LocalDNS configuration for instructions on how to disable LocalDNS with NAP.

Disabling LocalDNS is an advanced operation and is generally not recommended for AKS Automatic production defaults unless you have a validated exception.

To disable LocalDNS for a node pool, you must update your localdnsconfig.json file by setting the mode property to Disabled. This change instructs AKS to turn off the local DNS proxy on all nodes in the specified pool, reverting DNS resolution to the default cluster behavior. After updating the configuration file, apply it to the node pool using the Azure CLI to ensure the change takes effect.

az aks nodepool update --name mynodepool1 --cluster-name myAKSCluster --resource-group myResourceGroup --localdns-config ./localdnsconfig.json

Verify LocalDNS operation

In AKS Automatic, verification confirms the preconfigured LocalDNS baseline is active for your workloads. In AKS Standard, verification confirms your LocalDNS rollout.

Once LocalDNS is enabled, you can verify its operation by running DNS queries from pods in the specified node pool and inspecting the SERVER field in the responses to confirm LocalDNS addresses are returned (169.254.10.10 or 169.254.10.11).

Before running the validation steps, ensure the following conditions are met:

  1. You have kubectl installed and configured to access your AKS cluster.
  2. Your user account has sufficient permissions to create and exec into pods.
  3. The node pool where you want to validate LocalDNS is in a Ready state.
  4. The BusyBox image (busybox:1.28) is accessible from your cluster nodes.

Validation example:

  1. Create a debug pod in the node pool where LocalDNS is enabled:

    kubectl run dnstest --image=busybox:1.28 -- sleep 3600
    
  2. Once the pod is running, execute the following command to check DNS resolution:

    kubectl exec -it dnstest -- nslookup kubernetes.default
    

    Check the output. If localDNS is working correctly, you should see a response with the server address of 169.254.10.10 or 169.254.10.11:

    Server:    169.254.10.10
    Address 1: 169.254.10.10
    
    Name:      kubernetes.default
    Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
    

Configure LocalDNS

Note

If you're using Node Auto-Provisioning (NAP), see LocalDNS configuration for instructions on how to configure LocalDNS with NAP.

LocalDNS uses a JSON-based configuration file localdnsconfig.json to define DNS resolution behavior for each node pool. This file allows you to specify operational modes, server blocks for different DNS domains, and plugin settings such as caching, forwarding, and logging.

Default LocalDNS configuration

In AKS Automatic, LocalDNS is preconfigured. Use customization only when you have a specific requirement.

In AKS Standard, this default configuration is a good starting point.

When customizing LocalDNS, use the following configuration format as your template. You can define extra server blocks as needed, but adding unsupported or nonstandard top-level properties to the configuration results in validation failures.

{
  "mode": "Required",
  "vnetDNSOverrides": {
    ".": {
      "queryLogging": "Error",
      "protocol": "PreferUDP",
      "forwardDestination": "VnetDNS",
      "forwardPolicy": "Sequential",
      "maxConcurrent": 1000,
      "cacheDurationInSeconds": 3600,
      "serveStaleDurationInSeconds": 3600,
      "serveStale": "Immediate"
    },
    "cluster.local": {
      "queryLogging": "Error",
      "protocol": "ForceTCP",
      "forwardDestination": "ClusterCoreDNS",
      "forwardPolicy": "Sequential",
      "maxConcurrent": 1000,
      "cacheDurationInSeconds": 3600,
      "serveStaleDurationInSeconds": 3600,
      "serveStale": "Immediate"
    }
  },
  "kubeDNSOverrides": {
    ".": {
      "queryLogging": "Error",
      "protocol": "PreferUDP",
      "forwardDestination": "ClusterCoreDNS",
      "forwardPolicy": "Sequential",
      "maxConcurrent": 1000,
      "cacheDurationInSeconds": 3600,
      "serveStaleDurationInSeconds": 3600,
      "serveStale": "Immediate"
    },
    "cluster.local": {
      "queryLogging": "Error",
      "protocol": "ForceTCP",
      "forwardDestination": "ClusterCoreDNS",
      "forwardPolicy": "Sequential",
      "maxConcurrent": 1000,
      "cacheDurationInSeconds": 3600,
      "serveStaleDurationInSeconds": 3600,
      "serveStale": "Immediate"
    }
  }
}

Configure the mode for LocalDNS

LocalDNS can be enabled in three possible modes that define the extent of enforcement of LocalDNS for the workload:

  • Required: LocalDNS is enforced on the node pool if all prerequisites are satisfied. If the requirements aren't met, the deployment fails.
  • Disabled: Disables the local DNS feature, so DNS queries aren't resolved locally on the node.
  • Preferred: AKS validates that your LocalDNS configuration is syntactically correct but doesn't enable LocalDNS on the nodes. However, applying this mode still triggers a node reimage operation, so you can test your configuration for errors without affecting DNS resolution in your cluster.

For production workloads in AKS Automatic, keep the preconfigured LocalDNS behavior unless you have a validated need to customize.

The following table summarizes LocalDNS behavior for each mode and Kubernetes version:

Kubernetes version Preferred Required Disabled
Earlier than 1.31 Not supported Not supported Not supported
1.31 and later Config validated, not installed Installed and enforced Config validated, not installed

Note

The Preferred mode currently serves as a validation-only mode. In a future Kubernetes version, this mode will transition to automatically enabling LocalDNS. For production deployments today, use Required mode to enable LocalDNS.

Server blocks for LocalDNS

The default configuration applies to queries from pods using dnsPolicy:default (under vnetDNSOverrides) and pods using dnsPolicy:ClusterFirst (under kubeDNSOverrides). Within each, there are two default server blocks defined: . and cluster.local.

  • . represents all external DNS queries from pods that are trying to resolve public or non cluster domains (for example, microsoft.com).
  • cluster.local represents all internal Kubernetes service discovery queries from pods that are trying to resolve Kubernetes service names or internal cluster resources. These queries are routed through CoreDNS for resolution within the cluster.

Supported plugins for LocalDNS configuration

Plugin Description Default Allowed inputs
queryLogging Define the logging level for DNS queries. Error Error Log
protocol Sets the protocol used for DNS queries (UDP/TCP preference). ForceTCP for cluster.local, else PreferUDP PreferUDP ForceTCP
forwardDestination Specifies the DNS server to forward queries to. ClusterCoreDNS for cluster.local and kubeDNS traffic, else VnetDNS VnetDNS ClusterCoreDNS
forwardPolicy Determines the policy to use when selecting the upstream DNS server. Sequential Random RoundRobin Sequential
maxConcurrent Maximum number of concurrent DNS queries handled by LocalDNS. 1000 Integer
cacheDurationInSeconds Maximum TTL (Time To Live) in seconds for which DNS responses are cached. 3600 Integer
serveStaleDurationInSeconds Duration (in seconds) to serve stale DNS responses if upstream is unavailable. 3600 Integer
serveStale Policy for serving stale DNS responses during upstream failures. Immediate Verify Immediate Disabled

Configuration validation rules

When creating your LocalDNS configuration, be aware of these validation rules to avoid deployment failures:

  • Root zone (.) restrictions: Under vnetDNSOverrides, the forwardDestination for the root zone can't be ClusterCoreDNS.
  • Cluster.local zone restrictions: Under both vnetDNSOverrides and kubeDNSOverrides, the forwardDestination for cluster.local can't be VnetDNS.
  • Protocol and serveStale compatibility: When protocol is set to ForceTCP, serveStale can't be set to Verify. Use Immediate instead.

Note

These validation rules are enforced during configuration deployment. Violating them causes the LocalDNS configuration to fail validation.

Create a custom server block in LocalDNS

CoreDNS matches queries to a specific server block based on an exact match for domain being queried and not on partial matches. If you have the need for custom server blocks, you can add them to your LocalDNS configuration by creating a file called localdnsconfig.json with the added configurations.

For example, if you have specific DNS needs when accessing microsoft.com, you could use the following server block:

"microsoft.com": {
  "queryLogging": "Error",
  "protocol": "ForceTCP",
  "forwardDestination": "ClusterCoreDNS",
  "forwardPolicy": "Sequential",
  "maxConcurrent": 1000,
  "cacheDurationInSeconds": 3600,
  "serveStaleDurationInSeconds": 3600,
  "serveStale": "Immediate"
}

Monitor LocalDNS

In AKS Automatic, LocalDNS is preconfigured, so start with baseline validation and monitor behavior before applying custom tuning.

LocalDNS exposes Prometheus metrics that you can use for monitoring and alerting. Metrics are exposed on port 9253 of the node IP.

Example scrape configuration for Azure Managed Prometheus add-on as a DaemonSet:

kind: ConfigMap
apiVersion: v1
metadata:
  name: ama-metrics-prometheus-config-node
  namespace: kube-system
data:
  prometheus-config: |-
    global:
      scrape_interval: 1m
    scrape_configs:
    - job_name: localdns-metrics
      scrape_interval: 1m
      scheme: http
      metrics_path: /metrics
      relabel_configs:
      - source_labels: [__metrics_path__]
        regex: (.*)
        target_label: metrics_path
      - source_labels: [__address__]
        replacement: '$NODE_NAME'
        target_label: instance
      static_configs:
      - targets: ['$NODE_IP:9253']

Troubleshoot LocalDNS

DNS queries to specific domains are failing

If DNS queries to specific domains are failing after enabling LocalDNS:

  1. Check if you have domain-specific overrides in your localdnsconfig.json that might be misconfigured.
  2. Temporarily try removing domain-specific overrides and using only the default . configuration.
  3. Check if the issue occurs with both User Datagram Protocol (UDP) and Transmission Control Protocol (TCP) by adjusting the protocol setting.

Update VNet DNS servers for LocalDNS

When you update custom DNS servers directly in the VNet configuration (using the Azure portal or CLI), the AKS cluster nodes don't automatically apply these changes. Updating DNS settings at the VNet level only informs the Network Resource Provider (NRP), but doesn't notify the AKS Resource Provider. As a result, AKS nodes continue to use the previous DNS server settings until you take further action.

To ensure AKS nodes pick up the new VNet DNS server settings:

  1. Update the VNet DNS configuration using the Azure portal or APIs as needed.

  2. Use the AKS Resource Provider to reimage the node pool, ensuring that the updated DNS settings are applied and retained:

    az aks nodepool upgrade --resource-group myResourceGroup --cluster-name myAKSCluster --name mynodepool --node-image-only
    

This process ensures the AKS Resource Provider is aware of the DNS changes and applies them to all nodes in the node pool.

Update Cilium Network Policies to allow DNS resolution with LocalDNS

If you deploy Cilium Network Policies in your cluster, you must explicitly allow pod egress to the LocalDNS IP addresses.

Network policies enforce a default‑deny model for destinations that aren’t specified, so DNS traffic to LocalDNS is blocked unless it’s explicitly permitted.

  • On Azure CNI Powered by Cilium <=v1.16 with k8s <=1.31, this can be achieved by a CIDR-based policy.
  • On Azure CNI Powered by Cilium >=v1.17 with K8s >=1.32, a Cilium Network Policy allowing egress to host entities can be used.

The following Cilium Network Policy can be used to allow the traffic across all versions:

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "allow-azure-dns-egress"
  namespace: default
spec:
  endpointSelector:
    matchLabels: {} # This selects ALL pods in the namespace
  egress:
    - toCIDR:
        - 169.254.10.0/24
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
            - port: "53"
              protocol: TCP
    - toEntities:
        - host
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
            - port: "53"
              protocol: TCP