Skip to main content

DNS Automation

DNS Automation (external-dns) keeps DNS records in sync with your apps. It runs ExternalDNS: a hostname on an Ingress, a Service or a Gateway API route becomes a record in your Route 53, Cloud DNS or Azure DNS zone, and the record goes when the resource goes. It writes with the cluster's own cloud identity, or with a Secret you create; AstroPulse never receives or stores a DNS credential.

When to use it​

  • You put apps on your domain. Without it, every hostname needs a record you create by hand and fix when the load balancer's address changes. A model behind an access policy waits meanwhile with "Waiting for DNS: <hostname> does not resolve yet. If nothing manages its record, create one that points at your gateway's load balancer." With it, the record follows the app.
  • Your certificates come from Let's Encrypt over HTTP. Without a record, the certificate waits with "Waiting for the certificate: the hostname does not resolve yet, so Let's Encrypt cannot check it over HTTP. Nothing to do: cert-manager retries by itself once the DNS record is live." With it, the record is published and Certificates issues the certificate once the name resolves.
  • You expose models through the Gateway. Turn on Gateway routes, and their hostnames get records like any other app.
  • Several clusters share one zone. Each marks its records with its own owner ID and touches only those. See Record cleanup and ownership.

You don't need it when you create and keep every record yourself, or when no app is served on a domain.

Before you start​

  • A DNS zone in Route 53, Cloud DNS or Azure DNS, and its name for Domain filter, such as example.com.
  • Access to that zone for the add-on. On an EKS cluster AstroPulse created, there is nothing to do. Anywhere else, set it up first: Give it access to your cloud.

In the console​

After Version, its steps are Settings, then Advanced and Resources. Identity joins them once Cloud access is Workload identity. Review runs the dry run, and Ask Nova at the top of the steps answers about the open step.

  1. Open the cluster from the cluster list, select the Add-ons tab, then Install Add-on, and pick DNS Automation.

  2. Pick the Provider; it starts on the cluster's own cloud. Enter the Domain filter, such as example.com, comma-separated for several zones.

    Open zoom view

    DNS Automation settings with the DNS provider and the domain filter

    DNS Automation settings with the DNS provider and the domain filterDNS Automation settings with the DNS provider and the domain filter
  3. Optional: TXT owner ID, Policy, Dry run, Gateway routes, and Cloud access: Workload identity with its Identity fields, or Secret with the Secret ref it signs in with.

    Open zoom view

    DNS Automation's TXT owner ID, record policy, dry run and Publish DNS for Gateway API routes settings

    DNS Automation's TXT owner ID, record policy, dry run and Publish DNS for Gateway API routes settingsDNS Automation's TXT owner ID, record policy, dry run and Publish DNS for Gateway API routes settings
  4. On Review, the dry run runs by itself; select Continue to deploy once it passes, then Deploy external-dns.

Configure DNS Automation: Route 53 and your domain

You should see​

The add-on reads Deployed. Then check it can write:

Terminal
astroctl infra k8s addons logs <cluster> external-dns --level Error --follow=false --interactive=false

An empty error log means it can reach your zone. A permission problem shows here as an error from your cloud's DNS service, such as AccessDenied, 403 or AuthorizationFailed. To publish one record by hand:

Terminal
kubectl annotate service <service> external-dns.alpha.kubernetes.io/hostname=test.example.com
dig +short test.example.com

Within a minute or two the name resolves to the Service's address, with a TXT record beside it that marks it as this cluster's.

Open zoom view

The DNS Automation add-on reading Deployed, with its health check passing

The DNS Automation add-on reading Deployed, with its health check passingThe DNS Automation add-on reading Deployed, with its health check passing

Give it access to your cloud​

DNS Automation runs as service account external-dns in namespace external-dns. Everything below gives DNS permission to that one account and nothing else.

ClusterWhat to do
EKS, created by AstroPulseNothing. A Route 53 role for this add-on is created with the cluster and used automatically, limited to your Domain filter, or to your organization's verified domains when you set none
GKE or AKS, created by AstroPulseSet it up yourself, below. AstroPulse creates no DNS access on these clouds, and the install is refused until you name an identity or a secretRef
Registered, any cloudSet it up yourself, below. On AWS the install is refused until you name an identity or a secretRef

An identity or secretRef you write always wins over the automatic one. The rules every add-on shares are in Cloud access for add-ons. On EKS and GKE, the cluster needs workload identity first: Enable workload identity on your cluster.

EKS​

Give external-dns an IAM role that can change records in your hosted zone, and name it in identity. Full recipe: Let DNS Automation change records in one Route 53 zone on EKS.

GKE​

Name a Google service account that can write the zone (roles/dns.admin) in identity, under the annotation iam.gke.io/gcp-service-account, and let external-dns/external-dns act as it through Workload Identity. Full recipe: Let DNS Automation change records in Cloud DNS on GKE.

AKS​

Name a managed identity with DNS Zone Contributor on the zone in identity: its client ID under the annotation azure.workload.identity/client-id, and the label azure.workload.identity/use: "true", with a federated credential for external-dns/external-dns. Or use a secretRef holding an azure.json for a service principal that can write the zone (below). Full recipe: Let DNS Automation change records in one Azure DNS zone on AKS.

A zone in another cloud​

When the zone is in another cloud than the cluster, or the cluster has no workload identity, use secretRef. It keeps a long-lived credential on the cluster: use one that can change records in that one zone, and rotate it. A DNS host other than Route 53, Cloud DNS or Azure DNS is not supported by this add-on.

Letting external-dns read a credential from a Secret​

identity binds a cloud workload identity by its annotations (and, on Azure, labels), and nothing secret is stored:

external-dns.yaml
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: external-dns
spec:
clusterName: eks-prod
configuration:
externalDns:
provider: aws
domainFilter: example.com
identity:
serviceAccountAnnotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/dns-writer

secretRef is the name of a Secret you create yourself in the external-dns namespace, before you apply the add-on. AstroPulse stores the name and never reads the Secret:

external-dns.yaml
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: external-dns
spec:
clusterName: eks-prod
configuration:
externalDns:
provider: gcp
domainFilter: example.com
secretRef: clouddns-credentials
providerKey the Secret holds
awscredentials
gcpcredentials.json
azureazure.json

Set one of identity and secretRef, never both. An identity only binds on the cluster's own cloud.

Security: with identity, no DNS key exists on the cluster to leak or rotate, and your cloud revokes it in one place. With secretRef, AstroPulse stores only the Secret's name and never reads it. The role AstroPulse creates on EKS may change records only under your Domain filter, or your verified domains when you set none.

Record cleanup and ownership​

With Policy Sync (what a new add-on runs), DNS Automation creates, updates and deletes records to match the cluster: when an app goes, its record goes. It touches only records it owns, which it marks with a TXT record carrying its TXT owner ID.

  • Shared zone, no deletes. UpsertOnly creates and updates but never deletes, for a zone that also holds records you keep by hand. A stale record then keeps pointing at an address your cloud can give to someone else, so remove those yourself.
  • One owner per cluster. Two clusters with the same owner ID on one zone delete each other's records. Left empty, a first install takes the cluster's name followed by a suffix unique to the cluster, and keeps it.
  • Keep the owner of a running install. Records it already owns would no longer be managed.

Put the hostname on an Ingress host, on a Service with the external-dns.alpha.kubernetes.io/hostname annotation, or on an HTTPRoute with Gateway routes on. It must fall under the Domain filter, or your verified domains when you set none.

Run it in production​

  • One copy, by design. It has no leader election, so it runs one pod. While it is down, records stop updating but are not deleted; it catches up when it returns.
  • It checks every minute. A new or changed application's record appears within about a minute of its address.
  • Who owns which record decides what it can delete: Record cleanup and ownership. Use UpsertOnly on a zone that other tools also write to.

Other ways in​

  • Nova: ask it, for example "install DNS Automation for example.com on eks-prod in dry-run mode".
  • SDK and API: the same operations, in the API reference.

With astroctl​

external-dns.yaml
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: external-dns
spec:
clusterName: eks-prod
configuration:
externalDns:
domainFilter: example.com
dryRun: true
Terminal
astroctl infra k8s addons apply -f external-dns.yaml --dry-run
astroctl infra k8s addons apply -f external-dns.yaml

With dryRun: true it logs what it would publish and writes nothing. Confirm from the logs that it sees your zone, then apply again with dryRun off.

Settings​

Its own settings, under externalDns, plus availability and resources.

ProviderexternalDns.provider

The DNS service to publish through. Left out, it is the cluster's own cloud. Required on a registered cluster whose cloud AstroPulse does not know.

Accepts
  • aws AWS Route 53 Publishes to Amazon Route 53, as an IAM role that may change records in the zone.
  • gcp Google Cloud DNS Publishes to Cloud DNS, as a Google service account with the DNS Administrator role.
  • azure Azure DNS Publishes to Azure DNS, as an identity with the DNS Zone Contributor role.
Domain filterexternalDns.domainFilter

The DNS zone it may write to, such as example.com, comma-separated for several. A suffix, not a pattern.

Default
Empty: your organization's verified domains when it runs as the identity AstroPulse created, refused if you have none; with other credentials, every zone they can reach. Set it.
TXT owner IDexternalDns.txtOwnerId

Marks the records this cluster owns, so clusters sharing a zone don't delete each other's.

Default
The value it already uses on this cluster; a first install takes the cluster's name and a suffix unique to the cluster.
PolicyexternalDns.policy

What it may do to the records it owns. Left out, an update keeps the current one and a new add-on runs Sync.

Accepts
  • Sync Sync Creates, updates and deletes the records it owns.
  • UpsertOnly Upsert only Creates and updates records but never deletes them, so stale records stay for you to remove.
Dry runexternalDns.dryRun

Logs what it would publish without publishing it.

Default
Off.
Gateway routesexternalDns.gatewayRoutes

Also publishes the hostnames of Gateway API HTTPRoutes, beside the Ingress hostnames. Needs the Gateway API CRDs on the cluster.

Default
Off.
IdentityexternalDns.identity

Shown when Cloud access is Workload identity. A cloud workload identity that may change records in your zone: serviceAccountAnnotations on its service account, and serviceAccountLabels on its pods.

Default
On an EKS cluster AstroPulse created, that cluster's DNS role.
Secret refexternalDns.secretRef

Shown when Cloud access is Secret. The name of a Secret in the external-dns namespace holding DNS credentials, where a workload identity can't be used. Not with identity.

If it's refused​

"external-dns publishes to Route 53 with AWS credentials of its own, and cluster "<cluster>" has none AstroPulse created: set configuration.externalDns.identity.serviceAccountAnnotations to the IAM role for service accounts it should run as ..."
Cause
A registered cluster on AWS, with no identity or secretRef.
Fix
Name an IAM role in identity, or a Secret in secretRef, as the message says.
"external-dns publishes to <Cloud DNS or Azure DNS> with an identity of its own, and AstroPulse does not create one for cluster "<cluster>". To publish records, ..."
Cause
A GKE or AKS cluster AstroPulse created, with no identity or secretRef.
Fix
Do what the message says: name the workload identity in identity, or a Secret in secretRef.
"identity and secretRef cannot be combined: identity binds external-dns to a cloud workload identity, secretRef names a Secret it reads instead. Set one"
Cause
Both are set.
Fix
Keep one.
"external-dns names provider <provider>, but cluster <cluster> runs on <cloud>, and a workload identity does not cross clouds. Name a Secret with secretRef instead"
Cause
An identity for a zone in another cloud.
Fix
Use secretRef for that zone.
"provider "<provider>" is not supported: use aws, gcp or azure, or leave it out to follow the cluster's cloud"
Cause
Another value in provider.
Fix
Use aws, gcp or azure, or leave it out.
"external-dns needs a DNS provider and the cloud of cluster "<cluster>" is not known: set configuration.externalDns.provider to aws, gcp or azure"
Cause
A registered cluster whose cloud AstroPulse does not know, with no provider.
Fix
Set Provider.
"external-dns publishes as the identity AstroPulse created with cluster "<cluster>", which may change DNS records only under the domains you name, and none is named: ..."
Cause
No Domain filter, and your organization has no verified domain.
Fix
Set Domain filter to your zone, or add and verify a domain for your organization, then apply again.

Next​