AKS’s default DNS resolution behavior is to forward all DNS queries to the Azure DNS service.

Let’s see how to break that.

Imagine you have a Kubernetes cluster running in Azure Kubernetes Service (AKS). By default, when a pod needs to resolve a hostname (like my-internal-service.mycompany.local), AKS forwards that request to Azure DNS. This is fine for public domains, but what if you have internal DNS zones you need to resolve, or you want to use your own DNS servers for specific domains? That’s where custom DNS configuration comes in.

Here’s a common scenario: you have an on-premises Active Directory environment with its own DNS servers that manage your internal domain names. You want your AKS pods to be able to resolve hostnames within that internal domain.

To achieve this, you’ll configure AKS to use your custom DNS servers. This involves setting up a DNS service within your AKS cluster that intercepts DNS requests and forwards them to your specified custom DNS servers based on defined rules.

Let’s walk through the configuration.

First, you need a DNS server that AKS can reach. This could be an Azure Private DNS Zone, a VM running a DNS server in your VNet, or even an on-premises DNS server accessible via VPN or ExpressRoute. For this example, let’s assume you have an on-premises DNS server at 10.1.1.53 that handles mycompany.local.

The core of custom DNS in AKS is the dnsService resource. You’ll deploy this as a Kubernetes deployment and service.

Here’s a minimal deployment for a DNS proxy (like coredns or dnsmasq). We’ll use coredns for this example, as it’s the default in AKS and highly configurable.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: custom-dns-proxy
  namespace: kube-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: custom-dns-proxy
  template:
    metadata:
      labels:
        app: custom-dns-proxy
    spec:
      containers:
      - name: coredns
        image: coredns/coredns:1.8.0 # Use a specific version
        args:
          - "-conf"
          - "/etc/coredns/Corefile"
        volumeMounts:
        - name: config-volume
          mountPath: /etc/coredns
      volumes:
      - name: config-volume
        configMap:
          name: custom-dns-config
          items:
          - key: Corefile
            path: Corefile
---
apiVersion: v1
kind: Service
metadata:
  name: custom-dns-service
  namespace: kube-system
  labels:
    k8s-app: kube-dns # This label is important for AKS to pick it up
spec:
  selector:
    app: custom-dns-proxy
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP

Next, you need to create the Corefile ConfigMap that tells coredns how to behave. This is where you specify your custom DNS servers.

apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-dns-config
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . 10.1.1.53 {  # Forward all other queries to your custom DNS server
           force_tcp
        }
        cache 30
        loop
        reload
        loadbalance
    }

In this Corefile:

  • .:53: This is the main configuration block, specifying that coredns listens on port 53 for all domains (.).
  • kubernetes cluster.local in-addr.arpa ip6.arpa: This block tells coredns to handle DNS queries for Kubernetes internal services (cluster.local).
  • forward . 10.1.1.53: This is the crucial part. It instructs coredns to forward all DNS queries that aren’t handled by the kubernetes plugin (i.e., not internal Kubernetes services) to the IP address 10.1.1.53. force_tcp is often useful for reliable forwarding.
  • cache 30: Caches DNS responses for 30 seconds.
  • reload: Allows coredns to reload its configuration without restarting.

After applying these YAMLs, you need to tell AKS to use this new DNS service. The default DNS service in AKS is kube-dns (which is typically coredns itself). By creating a service named kube-dns in the kube-system namespace with the k8s-app: kube-dns label, you’re essentially replacing the default DNS service with your custom one.

You can verify by deploying a test pod and checking its DNS resolution:

kubectl run test-dns --image=busybox:latest --restart=Never --rm -it -- sh
nslookup my-internal-service.mycompany.local

If 10.1.1.53 is reachable from your AKS cluster and correctly resolves my-internal-service.mycompany.local, your pod should receive the correct IP address.

The coredns pods you deployed will now act as the primary DNS resolver for your cluster. They’ll handle internal Kubernetes lookups and forward everything else to your specified custom DNS server.

One subtle point is the force_tcp directive in the forward plugin. While DNS is traditionally UDP-based, TCP is often preferred for forwarding to external servers, especially when dealing with larger DNS responses or when network intermediaries might drop UDP packets. It ensures more reliable resolution for those external lookups.

The next thing you’ll likely want to configure is DNS-based access control for specific domains, or even have multiple custom DNS servers for different domain suffixes.

Want structured learning?

Take the full Aks course →