ExternalDNS sources support a number of annotations on the Kubernetes resources that they examine.
The following table documents which sources support which annotations:
| Source | controller | hostname | internal-hostname | target | ttl | (provider-specific) |
|---|---|---|---|---|---|---|
| Ambassador | Yes | Yes | Yes | |||
| Connector | ||||||
| Contour | Yes | Yes1 | Yes | Yes | Yes | |
| CRD | ||||||
| F5 | Yes | Yes | ||||
| Gateway | Yes | Yes1 | Yes2 | Yes | Yes | |
| Gloo | Yes | Yes3 | Yes3 | |||
| Ingress | Yes | Yes1 | Yes | Yes | Yes | |
| Istio | Yes | Yes1 | Yes | Yes | Yes | |
| Kong | Yes1 | Yes | Yes | Yes | ||
| Node | Yes | Yes | Yes | |||
| OpenShift | Yes | Yes1 | Yes | Yes | Yes | |
| Pod | Yes | Yes | Yes | |||
| Service | Yes | Yes1 | Yes14 | Yes5 | Yes | Yes |
| Skipper | Yes | Yes1 | Yes | Yes | Yes | |
| Traefik | Yes1 | Yes | Yes | Yes |
Specifies which set of node IP addresses to use for a Service of type NodePort.
If the value is public, use the Nodes' addresses of type ExternalIP, plus IPv6 addresses of type InternalIP.
If the value is private, use the Nodes' addresses of type InternalIP.
If the annotation is not present and there is at least one address of type ExternalIP,
behave as if the value were public, otherwise behave as if the value were private.
If this annotation exists and has a value other than dns-controller then the source ignores the resource.
Specifies which set of addresses to use for a headless Service.
Supported values:
NodeExternalIP. Required--service-type-filter=ClusterIPand--service-type-filter=Nodeor no--service-type-filterflag specified.HostIP.
If the value is NodeExternalIP, use each relevant Pod's Node's address of type ExternalIP
plus each IPv6 address of type InternalIP.
Otherwise, if the value is HostIP or the --publish-host-ip flag is specified, use
each relevant Pod's Status.HostIP.
Otherwise, use the IP of each of the Service's Endpoints's Addresses.
Specifies additional domains for the resource's DNS records.
Multiple hostnames can be specified through a comma-separated list, e.g.
svc.mydomain1.com,svc.mydomain2.com.
For Pods, uses the Pod's Status.PodIP, unless they are hostNetwork: true in which case the NodeExternalIP is used for IPv4 and NodeInternalIP for IPv6.
Notes:
- This annotation can override or add extra hostnames alongside any automatically derived hostnames (e.g., from Ingress.spec.rules[].host).
- The
ingress-hostname-sourceannotation may be used to specify where to get the domain for anIngressresource. - Hostnames must match the domain filter set in ExternalDNS (e.g., --domain-filter=example.com).
- This is an alpha annotation — subject to change; newer versions may support alternatives or deprecate it.
- This annotation is helpful for:
- Services or other resources without native hostname fields.
- Explicit overrides or multi-host situations.
- Avoiding reliance on auto-detection or heuristics.
You have a Service (e.g. of type LoadBalancer or ClusterIP) and want to expose it under a custom DNS name:
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
external-dns.kubernetes.io/hostname: app.example.com
spec:
type: LoadBalancer
...ExternalDNS will create a A or CNAME record for app.example.com pointing to the external IP or hostname of the service.
You can assign multiple hostnames by separating them with commas:
annotations:
external-dns.kubernetes.io/hostname: api.example.com,api.internal.example.comExternalDNS will create two DNS records for the same service.
When using Ingress, you usually declare hostnames in the spec.rules[].host. But with this annotation, you can manage DNS independently:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
external-dns.kubernetes.io/hostname: www.example.com
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80Useful when DNS management is decoupled from routing logic.
Specifies where to get the domain for an Ingress resource.
If the value is defined-hosts-only, use only the domains from the Ingress spec.
If the value is annotation-only, use only the domains from the Ingress annotations.
If the annotation is not present, use the domains from both the spec and annotations.
This annotation allows ExternalDNS to work with Istio & GlooEdge Gateways that don't have a public IP.
It can be used to address a specific architectural pattern, when a Kubernetes Ingress directs all public traffic to an Istio or GlooEdge Gateway:
-
The Challenge: By default, ExternalDNS sources the public IP address for a DNS record from a Service of type LoadBalancer. However, in some setups, the Gateway's Service is of type ClusterIP, with all public traffic routed to it via a separate Kubernetes Ingress object. This setup leaves the Gateway without a public IP that ExternalDNS can discover.
-
The Solution: The annotation on the Istio/GlooEdge Gateway tells ExternalDNS to ignore the Gateway's Service IP. Instead, it directs ExternalDNS to a specified Ingress resource to find the target LoadBalancer IP address.
apiVersion: gateway.solo.io/v1
kind: Gateway
metadata:
annotations:
external-dns.kubernetes.io/ingress: gateway-proxy
labels:
app: gloo
name: gateway-proxy
namespace: gloo-system
spec:
bindAddress: '::'
bindPort: 8080
options: {}
proxyNames:
- gateway-proxy
ssl: false
useProxyProto: false
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gateway-proxy
namespace: gloo-system
spec:
ingressClassName: alb
rules:
- host: cool-service.example.com
http:
paths:
- backend:
service:
name: gateway-proxy
port:
name: http
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- hostname: k8s-alb-c4aa37c880-740590208.us-east-1.elb.amazonaws.com
---
# This object is generated by GlooEdge Control Plane from Gateway and VirtualService.
# We have no direct control on this resource
apiVersion: gloo.solo.io/v1
kind: Proxy
metadata:
labels:
created_by: gloo-gateway
name: gateway-proxy
namespace: gloo-system
spec:
listeners:
- bindAddress: '::'
bindPort: 8080
httpListener:
virtualHosts:
- domains:
- cool-service.example.com
metadataStatic:
sources:
- observedGeneration: "6652"
resourceKind: '*v1.VirtualService'
resourceRef:
name: cool-service
namespace: gloo-system
name: cool-service
routes:
- matchers:
- prefix: /
metadataStatic:
sources:
- observedGeneration: "6652"
resourceKind: '*v1.VirtualService'
resourceRef:
name: cool-service
namespace: gloo-system
upgrades:
- websocket: {}
metadataStatic:
sources:
- observedGeneration: "6111"
resourceKind: '*v1.Gateway'
resourceRef:
name: gateway-proxy
namespace: gloo-system
name: listener-::-8080
useProxyProto: falseSpecifies the domain for the resource's DNS records that are for use from internal networks.
For Services of type LoadBalancer, uses the Service's ClusterIP.
For Pods, uses the Pod's Status.PodIP.
Use this annotation when you want an internal DNS name that resolves to the Service ClusterIP, for
in-cluster workloads or private network clients.
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
external-dns.kubernetes.io/internal-hostname: my-service.internal.example.com
spec:
type: LoadBalancer
...ExternalDNS will create an internal DNS record for
my-service.internal.example.comtargeting the ServiceClusterIP.
Use this annotation on a Pod when you want an internal DNS name that resolves to that Pod's Status.PodIP.
apiVersion: v1
kind: Pod
metadata:
name: my-pod
annotations:
external-dns.kubernetes.io/internal-hostname: my-pod.internal.example.com
spec:
...ExternalDNS will create an internal DNS record for
my-pod.internal.example.comtargeting the PodStatus.PodIP.
Controls load balancer hostname targets with CNAME records.
When the annotation is set to "true" the CNAME records are resolved to their underlying
A/AAAA records at sync time, ExternalDNS performs a DNS lookup for each hostname target and emits the
resulting IP addresses as A and/or AAAA endpoints. If resolution fails (e.g. the hostname is
temporarily unresolvable), that target is silently skipped.
This is useful when a DNS provider does not support CNAME flattening or ALIAS records and a flat IP address record is required.
Resolve load balancer hostnames to IPs for one Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: default
annotations:
external-dns.alpha.kubernetes.io/resolve-target: "true"
spec:
rules:
- host: svc.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
status:
loadBalancer:
ingress:
- hostname: example.comExternalDNS will resolve the Ingress load balancer hostname and publish A/AAAA records instead of a CNAME. Let's take an example, "example.com" CNAME resolves to A record "104.20.23.154" and AAAA record "2606:4700:10::ac42:93f3". ExternalDNS will create an A record for "svc.example.com" with target "104.20.23.154" and an AAAA record for "svc.example.com" with target "2606:4700:10::ac42:93f3".
Specifies a comma-separated list of values to override the resource's DNS record targets (RDATA).
Targets that parse as IPv4 addresses are published as A records and targets that parse as IPv6 addresses are published as AAAA records. All other targets are published as CNAME records.
Specifies the TTL (time to live) for the resource's DNS records.
The value may be specified as either a duration or an integer number of seconds.
It must be between 1 and 2,147,483,647 seconds.
Note; setting the value to
0means, that TTL is not configured and thus use default.
Specifies where to get the domain for a Route resource. This annotation should be present on the actual Route resource, not the Gateway resource itself.
If the value is defined-hosts-only, use only the domains from the Route spec.
If the value is annotation-only, use only the domains from the Route annotations.
If the annotation is not present, use the domains from both the spec and annotations.
Controls whether ExternalDNS creates additional record types for a resource's A/AAAA endpoints.
Supported values:
"ptr"— create PTR records for this resource (opt in).""(empty) — do not create additional records (opt out when--create-ptris enabled).
This annotation overrides the --create-ptr CLI flag per the
standard configuration precedence.
See Automatic PTR (Reverse DNS) Records for full documentation.
Some providers define their own annotations. Cloud-specific annotations have keys prefixed as follows:
| Cloud | Annotation prefix |
|---|---|
| AWS | external-dns.kubernetes.io/aws- |
| Azure | external-dns.kubernetes.io/azure- |
| CloudFlare | external-dns.kubernetes.io/cloudflare- |
| Scaleway | external-dns.kubernetes.io/scw- |
Additional annotations implemented by specific providers:
If the value of this annotation is true, specifies that CNAME records generated by the
resource should instead be alias records.
Additionally, you can set the value to A or AAAA to create only one type of alias record:
A: Creates only an A alias record (IPv4 only)AAAA: Creates only an AAAA alias record (IPv6 only)
This is useful when your alias target is IPv4-only (i.e., it does not have an AAAA target), and creating an AAAA alias record would fail.
Note: The A and AAAA values are currently only supported by the AWS Route53 provider.
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: default
annotations:
external-dns.alpha.kubernetes.io/hostname: app.example.com
external-dns.alpha.kubernetes.io/target: ipv4-only-target.example.com
# Create only an A (IPv4) alias record to avoid creating an AAAA alias record for an IPv4-only target.
external-dns.alpha.kubernetes.io/alias: "A"
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: my-appThis annotation is only supported on A, AAAA, and CNAME record types. Endpoints with other record types (e.g. MX, SRV, TXT) that have this annotation set will be rejected.
Supported providers:
- AWS: This annotation is only relevant if the
--aws-prefer-cnameflag is specified. - PowerDNS: When this annotation is set to
true, CNAME records will be created as ALIAS records. This is useful when using PowerDNS withexpand-alias=yesto resolve CNAME targets to IP addresses on the authoritative server side. Alternatively, use the--prefer-aliasflag to convert all CNAME records to ALIAS globally.
Specifies the set identifier for DNS records generated by the resource.
A set identifier differentiates among multiple DNS record sets that have the same combination of domain and type. Which record set or sets are returned to queries is then determined by the configured routing policy.
Required for AWS Route53 routing policies (weighted, latency, failover, geolocation, geoproximity, multi-value). See the AWS tutorial — Routing policies for the full list of annotations and examples.
Notes:
- The annotation is provider-agnostic in design but is primarily used with AWS Route53 routing policies.
- The value is arbitrary but must be unique per record set for the same domain and type combination.
- For Gateway API sources, this annotation must be placed on Route resources (e.g.,
HTTPRoute), not on theGatewayresource itself. See Gateway API Annotation Placement.
When using Gateway API, place set-identifier on the Route resource, not the Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
annotations:
# target goes on the Gateway
external-dns.kubernetes.io/target: "alb-123.us-east-1.elb.amazonaws.com"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route
annotations:
# set-identifier and routing policy go on the Route
external-dns.kubernetes.io/set-identifier: backend-v1
external-dns.kubernetes.io/aws-weight: "100"
spec:
parentRefs:
- name: my-gateway
hostnames:
- app.example.comPlacing
set-identifieron the Gateway instead of the Route is a common mistake — the Gateway source only reads thetargetannotation.
When using Gateway API sources (gateway-httproute, gateway-grpcroute, gateway-tlsroute, etc.), annotations
are read from different resources: Gateway resource reads only the target annotation, while Route resources
(HTTPRoute, GRPCRoute, TLSRoute, etc.) read all other annotations (hostname, ttl, controller,
resolve-target, and provider-specific annotations like cloudflare-*, aws-*, scw-*).
ListenerSet resources also support the target annotation. When a Route references a ListenerSet
as its parent, the ListenerSet's target annotation takes precedence over the parent Gateway's target annotation.
ListenerSet support requires the --gateway-listener-sets flag to be enabled.
For more details and comprehensive examples, see the Gateway API documentation.
Footnotes
-
Unless the
--ignore-hostname-annotationflag is specified. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 -
For Gateway API sources, annotation placement differs by type. See Gateway API Annotation Placement for details. ↩
-
The annotation must be on the listener's
VirtualService. ↩ ↩2 -
Only behaves differently than
hostnameforServices of typeClusterIPorLoadBalancer. ↩ -
Also supported on
Podsreferenced from a headlessService'sEndpoints. ↩