Skip to content

RFC2136: CNAME records from DNSEndpoint CRDs are removed and re-added every sync cycle #6199

@CerebralXor

Description

@CerebralXor

What happened:

CNAME records managed via the DNSEndpoint CRD are constantly removed and re-added on every sync cycle, even though the records and their values have not changed. Both the CNAME records themselves and the associated TXT ownership records are affected.

This only affects records created from DNSEndpoint CRDs. Records created from Ingress manifests do not exhibit this behaviour. Additionally, A and AAAA records managed via DNSEndpoint CRDs are not affected — only CNAME records.

What you expected to happen:

After the initial creation of the DNS records, subsequent sync cycles should detect that the records already exist with the correct values and make no changes.

How to reproduce it (as minimally and precisely as possible):

  • Deploy ExternalDNS v0.17.0 with the RFC2136 provider using AXFR (--rfc2136-tsig-axfr), sync policy, and TXT registry
  • Create a DNSEndpoint CRD with a CNAME record:
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
  name: myservice
spec:
  endpoints:
  - dnsName: myservice.example.com
    recordTTL: 3600
    recordType: CNAME
    targets:
    - traefik.example.com
  • Observe that every sync cycle removes and re-adds the CNAME and its TXT ownership records with identical values.

ExternalDNS Deployment args:

args:
  - --events
  - --interval=10m
  - --source=ingress
  - --source=crd
  - --source=traefik-proxy
  - --provider=rfc2136
  - --rfc2136-host=<dns-server-host>
  - --rfc2136-port=53
  - --rfc2136-zone=example.com
  - --domain-filter=example.com
  - --rfc2136-tsig-keyname=external-dns
  - --rfc2136-tsig-secret-alg=hmac-sha256
  - --rfc2136-tsig-axfr
  - --policy=sync
  - --traefik-disable-legacy
  - --registry=txt
  - --txt-wildcard-replacement=wildcard
  - --log-level=debug

Sanitised logs showing the remove/add loop (repeats every sync):

time="2026-02-16T01:53:19Z" level=debug msg="Fetching records for '\"example.com\"'"
time="2026-02-16T01:53:19Z" level=debug msg="Fetching records from nameserver: <dns-server>:53"
time="2026-02-16T01:53:19Z" level=debug msg="Record=example.com.\t900\tIN\tSOA\tns1.example.com. hostadmin.example.com. 1161 900 300 604800 900"
time="2026-02-16T01:53:19Z" level=debug msg="Record=a-traefik.example.com.\t0\tIN\tTXT\t\"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/traefik\""
time="2026-02-16T01:53:19Z" level=debug msg="Record=traefik.example.com.\t3600\tIN\tA\t10.0.0.10"
time="2026-02-16T01:53:19Z" level=debug msg="Record=myservice.example.com.\t3600\tIN\tCNAME\ttraefik.example.com."
time="2026-02-16T01:53:19Z" level=debug msg="Record=myservice.example.com.\t0\tIN\tTXT\t\"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/myservice\""
time="2026-02-16T01:53:19Z" level=debug msg="Record=cname-myservice.example.com.\t0\tIN\tTXT\t\"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/myservice\""
time="2026-02-16T01:53:19Z" level=info msg="Removing RR: myservice.example.com 3600 CNAME traefik.example.com"
time="2026-02-16T01:53:19Z" level=info msg="Adding RR: myservice.example.com 3600 CNAME traefik.example.com"
time="2026-02-16T01:53:19Z" level=info msg="Removing RR: myservice.example.com 0 TXT \"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/myservice\""
time="2026-02-16T01:53:19Z" level=info msg="Adding RR: myservice.example.com 0 TXT \"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/myservice\""
time="2026-02-16T01:53:19Z" level=info msg="Removing RR: cname-myservice.example.com 0 TXT \"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/myservice\""
time="2026-02-16T01:53:19Z" level=info msg="Adding RR: cname-myservice.example.com 0 TXT \"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/myservice\""

This pattern repeats on every event-triggered and interval-based sync for all CNAME DNSEndpoint CRDs.

Anything else we need to know?:

The DNS server is Technitium DNS.

Environment:

External-DNS version: v0.17.0 (registry.k8s.io/external-dns/external-dns:v0.17.0)
DNS provider: RFC2136 (Technitium DNS server)
Kubernetes: in-cluster deployment (K0s)
Platform: linux/amd64

Possible cause:

The AXFR response returns the CNAME target as a fully qualified domain name with a trailing dot (traefik.example.com.), which is standard DNS wire format. The DNSEndpoint CRD specifies the target without a trailing dot (traefik.example.com). It appears the RFC2136 provider may not be normalising the trailing dot when comparing current state against desired state, causing it to always detect a difference. A and AAAA records would not be affected since their targets are IP addresses, which don't have trailing dots in DNS responses.

Adding a trailing dot to the CNAME target in the DNSEndpoint CRD prevents the CNAME record being created and causes this warning to be logged:

level=warning msg="Endpoint ceph with DNSName myservice.example.com has an illegal target. The subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com')"

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions