fix(dns): cache floor + serve_stale so UDM blips stop resolving to the WAN proxy #435

Merged
k.renner merged 1 commits from claude-bot/dns-serve-stale into master 2026-07-27 20:19:00 +00:00
2 changed files with 41 additions and 2 deletions
@@ -15,9 +15,23 @@ data:
# (The old Proxmox DNS VIP 10.2.40.53 is DECOMMISSIONED.) 10.96.0.11 is a
# pinned ClusterIP of the k8s-gateway Service — avoids DNS-name
# resolution during CoreDNS startup.
# Cache tuning mirrors k8s-gateway's (issue #393): the UDM's dnsmasq
# refuses connections in bursts, and because the public renner.uno zone
# has a *.renner.uno wildcard CNAME that matches at every depth, a local
# failure never returns NXDOMAIN — it resolves to the WAN proxy instead.
# Correct answers carry TTL 5 (dnsmasq lease records) while the wrong
# wildcard answer carries TTL 300, so one brief blip pins a wrong answer
# for minutes. `success ... 60` raises the 5s TTL to a 60s floor and
# `serve_stale 1h immediate` keeps serving through an upstream outage.
# NOTE: only cache/serve_stale — the `template` plugin is deliberately NOT
# reintroduced here (it broke cluster DNS in 7a09bad5).
renner.uno:53 {
errors
cache 30
cache 300 {
success 9984 300 60
denial 9984 300 30
serve_stale 1h immediate
}
forward . 10.96.0.11
loop
reload
@@ -64,8 +64,33 @@ spec:
parameters: ":8080"
- name: ready
parameters: ":8181"
# Survive the UDM's intermittent DNS outages (issue #393).
#
# The UDM's dnsmasq refuses connections in bursts — 330 "connection
# refused" errors in the last 3000 log lines here, on BOTH 10.2.40.1 and
# fd00::1. That alone would only be a blip, but it combines with a
# wildcard trap: the public renner.uno zone has a *.renner.uno CNAME to
# proxy.renner.uno that matches at EVERY depth, so renner.uno never
# returns NXDOMAIN. Any name that fails locally resolves publicly to the
# WAN proxy instead of failing honestly.
#
# The TTLs make it stick: the correct answer is a dnsmasq lease record
# with TTL 5, the wrong wildcard answer has TTL 300 — 60x more durable.
# One 5-second UDM blip therefore pins a wrong answer for 5 minutes.
#
# `success ... 60` clamps that 5s TTL up to a 60s floor, and
# `serve_stale 1h immediate` keeps answering from cache while the
# upstream is refusing connections, so a blip stops becoming a lookup
# failure that the wildcard can capture.
#
# Written on ONE line deliberately: the chart interpolates `parameters`
# into its ConfigMap template, and a multi-line value breaks the YAML
# render ("could not find expected ':'"). Verified with `helm template`
# plus a real `k8s_gateway:1.7.0` container (CoreDNS 1.14.2) before
# merging. For the same reason the forward block is left untouched —
# its options need newlines and cannot be expressed here.
- name: cache
parameters: "30"
parameters: "300 { success 9984 300 60 denial 9984 300 30 serve_stale 1h immediate }"
- name: loop
- name: reload
- name: forward