Re-render breaks pod→CoreDNS cluster-wide until calico-node restart (iptables + nftables dataplanes, Canal/kdd, v3.31.4)
Expected Behavior
After a NetworkPolicy/endpoint re-render (e.g. a Helm upgrade of an unrelated
chart, a control-plane restart, or a large kubectl apply), pod→ClusterIP
traffic — in particular pod→CoreDNS (kube-dns) — should keep working.
Current Behavior
After a "substantial" re-render, pod→CoreDNS breaks cluster-wide: pods get
i/o timeout resolving any name against the kube-dns ClusterIP. The node
resolves fine against the same ClusterIP. The auto-generated CoreDNS ingress
policy (default-network-dns-policy, from: {} = allow-all on :53) is present
and correct, yet packets fall through to the tier-default DROP.
It does not self-recover — Felix's periodic refresh (60s) does not fix it.
Only a calico-node process restart heals it. Reproduces identically in the
iptables dataplane and the native nftables dataplane (nftablesMode: Enabled) → dataplane-agnostic.
Cascade: pods that (re)start and need DNS go CrashLoopBackOff; in-cluster registry pulls fail (ImagePullBackOff); ingress returns 503.
Steps to Reproduce
- RKE2 v1.34.6+rke2r1, Canal (Calico 3.31.4 policy-only + Flannel), single control-plane node, kdd datastore.
- Trigger a substantial Felix re-render (e.g. a HelmChartConfig that upgrades
the bundled ingress-nginx chart, or an
rke2-serverrestart, or a full app-stackkubectl apply). - From a pod:
getent hosts kubernetes.default.svc.cluster.local→ i/o timeout. From the node: same lookup vs the kube-dns ClusterIP → works. kubectl -n kube-system rollout restart ds rke2-canal→ immediately fixed.
Diagnostics
- Node→CoreDNS OK, pod→CoreDNS FAIL (pod-sourced policy path; CoreDNS pod healthy).
- conntrack not exhausted; iptables FORWARD default DROP shows 0 packets; policy
content correct via
describe. - iptables mode: broken traffic increments the cali
"End of tier default. Drop if no policies passed packet"DROP counter → the policy-ingress chain's accept is not matching. - nftables mode: same break → not a rendering-backend issue.
- Felix logs at Info show no error during the break.
- Felix's periodic refresh does not heal; only a process restart does → suggests in-process Felix state that incremental re-computation leaves wrong.
Environment
- Calico v3.31.4 (Rancher hardened-calico
v3.31.4-build2026032700), Canal (CALICO_NETWORKING_BACKEND=none,ClusterType: k8s,canal,kdd), kdd datastore. - RKE2 v1.34.6+rke2r1 (Kubernetes 1.34). Reproduced with kube-proxy=iptables (Calico iptables dataplane) AND kube-proxy=nftables (Calico nftables dataplane).
- OS Ubuntu, kernel 6.8.0-124-generic; iptables 1.8.11 (nf_tables); ipset v7.21
(protocol 7).
FELIX_IPTABLESBACKEND=auto.
Workaround (not production-acceptable)
Node-side watchdog that detects pod→CoreDNS breakage and runs
kubectl rollout restart ds rke2-canal. Bounded DNS-outage window per re-render.
Offer
Happy to provide Felix debug logs from a fresh reproduction, iptables-save
/ nft list ruleset before-vs-broken, and the full policy set on request.
Source: projectcalico/calico