external-dns v0.22 changed its default annotation prefix to external-dns.kubernetes.io/ with no fallback. Renovate #129 moved the chart to 1.23.0 (app v0.23.0), and at 12:13Z it ignored every alpha-prefixed annotation in this repo: the Gateway target (public names went to A 192.168.2.20), controller: none (jackett, pihole and semaphore got published), and the L4 Service hostnames (unifi and crafty-mc were deleted). Set annotationPrefix: external-dns.alpha.kubernetes.io/ to restore the v0.21 reading. Floor the chart at 1.23.0, because the flag needs app >= 0.22. Co-Authored-By: Claude Opus 5.5 <[email protected]>
5.8 KiB
date, branch, title
| date | branch | title |
|---|---|---|
| 2026-11-07 | fix/public-dns-wan-a-record | external-dns v0.23 ignored every alpha-prefixed annotation — pin annotationPrefix back |
What changed
platform/externaldns/helmrelease.yaml:
- adds
values.annotationPrefix: "external-dns.alpha.kubernetes.io/". The chart renders it as--annotation-prefix=. - tightens the chart range from
<1.24.0to>=1.23.0 <1.24.0, because that flag needs app= 0.22.
- adds a comment block explaining why.
Root cause. Renovate PR #129 (merged 12:12:00Z) moved the chart from >=1.21.0 <1.22.0 to
<1.24.0. Helm deployed chart 1.23.0 at 12:13:08Z, which moved the app from v0.21.0 to v0.23.0.
The upstream v0.22.0 release notes (#6424) say: "The default annotation prefix is now
external-dns.kubernetes.io/ with no fallback". Every external-dns annotation in this repo uses
external-dns.alpha.kubernetes.io/. At 12:13:12Z v0.23.0 therefore:
- ignored the Gateway's
target. Every public name flipped fromCNAME wan.home.keithjackson.devtoA 192.168.2.20, an RFC1918 address served in public DNS. - ignored
controller: none. The internal names jackett, pihole and semaphore were published. That breaks R-DNS-03. - ignored the L4 Services'
hostname. The unifi and crafty-mc A records were deleted.
cloudflare-ddns (#123, v1.18.0) is not involved. It logs only IPv4-enabled domains: wan.home.keithjackson.dev and has made no changes.
Why pin the prefix instead of migrating the annotations. The pin is one value in one file, and
that file is not on the sensitive-path list. It restores the exact v0.21 reading of target,
controller and hostname together. A migration would touch catalog/render.py, every
apps/wave-* file, the hand-written platform manifests, and the conformance goldens and checks.
conformance/** needs an operator override. And a migration half-applied in either direction
reproduces this incident.
I rejected --enable-legacy-annotation-prefix. Upstream calls it a migration aid that will be
removed.
Follow-up. The L0 guard that the tree's annotation prefix matches this value is a separate PR
(feat/l0-externaldns-annotation-prefix), because it lives in conformance/**. A full migration
to the GA prefix remains open. It must change the annotations and this value in one commit.
What I verified
- Live HelmRelease history.
1.23.0 0.23.0 lastDeployed 2026-11-07T12:13:08Z deployed, and before that1.21.1 0.21.0 ... superseded. The Deployment image isexternal-dns:v0.23.0. The startup config logsAnnotationPrefix:external-dns.kubernetes.io/ EnableLegacyAnnotationPrefix:false. - external-dns log, 12:13:12Z:
action=CREATE ... type=Aanda-TXT for gitea, crafty, the apex, headlamp, auth, jackett, nextcloud, keycloak, grafana, seerr, plex, pihole and semaphore.action=DELETE ... type=CNAMEandcname-TXT for all of those except jackett, pihole and semaphore.action=DELETE ... type=Afor unifi and crafty-mc.
- Before the upgrade. Loki covers about 11-05 13:00Z to 12:13Z. The old pod logged no
Changing recordline in that window. Its only error was one Cloudflare 502 on 11-05. - Gateway.
gateway/sharedstill carriesexternal-dns.alpha.kubernetes.io/target: wan.home.keithjackson.dev. - Public DNS. At the authority (betty.ns.cloudflare.com), 1.1.1.1 and 8.8.8.8:
- plex, gitea, auth, the apex, jackett, pihole and semaphore answer A
192.168.2.20. - unifi and crafty-mc have no record.
wan.answers A136.51.115.252.- There is no AAAA for any of these names.
- The
a-plexanda-jackettTXT records readexternal-dns/owner=prod.
- plex, gitea, auth, the apex, jackett, pihole and semaphore answer A
- Upstream v0.23.0 source.
controller/execute.gocallsannotations.SetAnnotationPrefix(cfg.AnnotationPrefix).SetAnnotationPrefixrebuildsTargetKey,ControllerKeyandHostnameKey.source/gateway.gostill readsTargetsFromTargetAnnotation(gw.Annotations).- Chart 1.23.0
templates/deployment.yamlrenders--annotation-prefix={{ .Values.annotationPrefix }}.
- Phone. Pi-hole 192.168.2.23 answers correctly: A
192.168.2.20with TTL 60, and AAAA NOERROR with no data. The router 192.168.2.1 answersgitea.with NOERROR and 0 answers, because it strips RFC1918 results. The workstation uses onlynameserver 192.168.2.23and hasdisable_ipv6 = 1. - Toolbox gate. See the PR description.
What I did NOT verify
- The fix live. This depends on Flux applying it. Expected on the first reconcile, with
policy: syncand ownerprod, all of these records being prod-owned:- DELETE the A record and create CNAME
wan.for the ten public names. - DELETE the A and TXT records for jackett, pihole and semaphore.
- CREATE A for unifi (.21) and crafty-mc (.22).
This is UNVERIFIED until someone reads the external-dns log and checks with
dig @betty.ns.cloudflare.com. The A-to-CNAME type swap went through cleanly on 2026-10-03, and the reverse went through today, so I expect it to converge. If it does not, see the manual list in the PR.
- DELETE the A record and create CNAME
- That the chart's schema accepts
annotationPrefixunder Flux. I did not render the HelmRelease with helm here. The gate checks YAML and policy, not the chart render. - The phone. Not investigated, by decision. Likely cause: the phone bypasses Pi-hole on the client side. Candidates are IPv6 RA/RDNSS DNS from the router, Android Private DNS and iCloud Private Relay. Answers it gets that way are rebind-stripped, or point to the WAN IP, which needs hairpin NAT. Hairpin looked absent on 2026-10-03 (a self-signed cert on WAN:443). This is router- or client-side, not in-repo.
- The AAAA records the operator saw on 8.8.8.8. I did not reproduce them.
- The cilium (#104) and pihole (#109) bumps were not examined beyond Pi-hole answering correctly.