The most surprising thing about Route 53 external DNS for EKS is that it’s not just about making your services resolvable; it’s about making your Kubernetes cluster aware of the external world through DNS, which unlocks powerful automation patterns.

Let’s say you have an EKS cluster running a web application. You want to expose it to the internet. Normally, you’d create a Kubernetes Service of type LoadBalancer, which provisions an AWS Elastic Load Balancer (ELB) and gives you a DNS name for it. You’d then manually go into Route 53 and create an A record pointing your custom domain (e.g., myapp.example.com) to that ELB DNS name. This works, but it’s fragile. If the ELB is replaced (e.g., during an upgrade or failure), its DNS name changes, and you have to update Route 53 manually.

This is where automation comes in. We can use the external-dns project to automatically manage Route 53 records based on Kubernetes Service and Ingress resources.

Here’s how it works:

  1. Install external-dns in your EKS cluster. You’ll typically deploy it as a Deployment within your cluster.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: external-dns
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          app: external-dns
      template:
        metadata:
          labels:
            app: external-dns
        spec:
          containers:
          - name: external-dns
            image: registry.k8s.io/external-dns/external-dns:v0.13.1 # Use the latest stable version
            args:
            - --source=service # Watch Kubernetes Services
            - --source=ingress # Watch Kubernetes Ingresses
            - --domain-filter=example.com # Only manage records for this domain
            - --provider=aws # Use AWS as the DNS provider
            - --aws-zone-type=public # Specify the type of Route 53 hosted zone
            - --registry=txt # Use TXT records for ownership verification (recommended)
            - --txt-owner-id=my-eks-cluster-123 # A unique identifier for your cluster
            - --log-level=info
            - --interval=1m # How often to sync DNS records
            env:
            - name: AWS_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  name: aws-credentials # A Kubernetes Secret containing your AWS credentials
                  key: aws_access_key_id
            - name: AWS_SECRET_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: aws-credentials
                  key: aws_secret_access_key
            - name: AWS_REGION
              value: "us-east-1" # Your AWS region
    

    You’ll also need to create the aws-credentials secret. This secret should contain aws_access_key_id and aws_secret_access_key keys with your AWS IAM user credentials. The IAM user needs permissions to list and modify DNS records in your Route 53 hosted zone.

  2. Create a Kubernetes Service of type LoadBalancer. When external-dns is running and configured, it will watch for Services with specific annotations.

    apiVersion: v1
    kind: Service
    metadata:
      name: my-webapp-service
      annotations:
        external-dns.alpha.kubernetes.io/hostname: myapp.example.com. # The desired DNS name
    spec:
      selector:
        app: my-webapp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
      type: LoadBalancer
    

    The external-dns.alpha.kubernetes.io/hostname annotation is the key here. It tells external-dns to create or update a Route 53 record for myapp.example.com that points to the ELB DNS name provisioned by this Service.

  3. Observe Route 53. After applying the Service, you’ll see a new A record appear in your example.com hosted zone in Route 53, pointing to the ELB’s DNS name (e.g., a1b2c3d4e5f6.us-east-1.elb.amazonaws.com). If the ELB is replaced, external-dns will detect the change in the ELB’s DNS name and automatically update the Route 53 record.

You can also use Ingress resources. If you’re using an Ingress controller (like AWS Load Balancer Controller), external-dns can manage DNS records for your Ingress hostnames.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-webapp-ingress
  annotations:
    external-dns.alpha.kubernetes.io/hostname: myapp.example.com.
    # Other ingress controller annotations
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-webapp-service
            port:
              number: 80

This pattern is incredibly powerful for maintaining a stable DNS entry for your applications, even as the underlying Kubernetes resources are scaled, updated, or replaced. The txt-owner-id combined with the registry=txt provider option is crucial for preventing external-dns from accidentally deleting records it doesn’t own. It writes a TXT record with its owner ID next to the managed DNS record, and before modifying or deleting a record, it checks for this TXT record’s existence.

While external-dns handles the creation of A/CNAME records, it doesn’t automatically manage the creation of the Route 53 Hosted Zone itself. You still need to manually create the primary Route 53 Hosted Zone for your domain (e.g., example.com) before external-dns can manage records within it.

Want structured learning?

Take the full Route53 course →