#13132·calico

Re-render breaks pod→CoreDNS cluster-wide until calico-node restart (iptables + nftables dataplanes, Canal/kdd, v3.31.4)

Author: litronicsCreated Jul 2, 2026Updated Sep 19, 2026

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

  1. RKE2 v1.34.6+rke2r1, Canal (Calico 3.31.4 policy-only + Flannel), single control-plane node, kdd datastore.
  2. Trigger a substantial Felix re-render (e.g. a HelmChartConfig that upgrades the bundled ingress-nginx chart, or an rke2-server restart, or a full app-stack kubectl apply).
  3. From a pod: getent hosts kubernetes.default.svc.cluster.local → i/o timeout. From the node: same lookup vs the kube-dns ClusterIP → works.
  4. 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.