In a Helm chart, why does `getHostByName` render an empty string?
answer
- It fails quietly rather than loudly
- A capability that must be asked for
- Ask which machine actually did the lookup
- Two runners, two manifests
- The answer is captured, not tracked
basics
~20 sHelm keeps DNS resolution switched off during rendering, so sprig's getHostByName returns an empty string unless the rendering command is given --enable-dns. Even then the lookup runs on the machine running helm, not inside the cluster.
solid answer
~50 s`getHostByName` comes from sprig and performs a DNS lookup, but Helm gates network resolution during a render: without `--enable-dns` it quietly yields an empty string rather than an error, so the field it feeds renders blank and the manifest is structurally valid and wrong. Passing `--enable-dns` turns it on, and that is where the real problem starts: the resolution happens wherever `helm` is running — a laptop, a CI runner — using that host's resolver and network, not the cluster's. A cluster-internal Service name will not resolve there, and an external name may resolve to a different address per region, so two runners produce two different manifests from the same chart and values. The address is also frozen into the rendered manifest at render time and never follows a change. Resolve names in the workload at runtime, or pass the address in as a value.
code
bash · 5 lines# renders empty when DNS is not enabled
helm template fanout ./fanout-batch --show-only templates/configmap.yaml
# same render with resolution turned on
helm template fanout ./fanout-batch --enable-dns --show-only templates/configmap.yamlgo deeper
Know that a Helm render does no DNS by default and that the function returns an empty string rather than failing. If a rendered address is blank, ask whether resolution was ever enabled.
Explain the mechanics: --enable-dns turns the lookup on, it runs on the host executing helm, and the result is written into the manifest at render time rather than looked up later.
Demonstrate the diagnosis and the judgement: distinguish resolution being off from a name that genuinely does not resolve on that host, and argue for resolving in the workload or passing the address as a value instead.
Own the rule that renders do not touch the network. Decide whether your platform permits the flag at all, since allowing it makes manifests depend on which agent rendered them and turns reproducible diffs into noise.
### The symptom A chart for a chat fan-out service wants to pin an upstream broker's address at install time, so a template does something like `brokerAddress: {{ getHostByName .Values.broker.host }}`. It renders, nothing errors, and the field comes out empty. The pod starts and dials nothing. Nothing in the output says why. ### The mechanism `getHostByName` is a sprig function that performs a DNS A-record lookup. Helm registers it, but it treats DNS resolution during a render as an opt-in capability: unless the rendering command is given `--enable-dns`, the lookup does not happen and the function returns an empty string. It does not raise an error, which is exactly why this costs someone an afternoon — an empty string flows through the pipeline and lands in the manifest as a blank value, or disappears entirely if it was piped into something that treats empty as absent. So the first fork in diagnosis is: was `--enable-dns` passed to whatever produced this manifest? A blank result is far more likely to mean "resolution is off" than "the name does not exist". ### Why it is off by default A render that performs network I/O stops being reproducible. Helm's rendering model is that a chart plus its values produce a manifest; the whole value of `helm template` in review, of comparing two renders, and of trusting that what you inspected is what gets applied, rests on the render being a pure function of its inputs. A DNS lookup makes the output depend on the resolver, the network and the moment. It also makes rendering fail in an air-gapped or egress-restricted environment for reasons that have nothing to do with the chart. ### Why enabling it rarely fixes the design Even with `--enable-dns`, three things remain true and each one is the actual interview point: 1. **The lookup runs on the rendering host.** Helm renders client-side. The resolver used is the one belonging to the machine running the command — a laptop, a CI runner, an agent. It is not the cluster's DNS, and it is not the resolver the pod will use. A Service name that resolves perfectly inside the cluster will not resolve on a build agent at all. 2. **The result depends on where you rendered.** An external name behind geo-aware DNS answers differently in different regions. Render the same chart and values on a runner in one region and on one in another and you get two manifests that differ in a field neither chart nor values mentions. Every subsequent diff of the release's stored manifest against a fresh render shows drift that is not drift. 3. **The address is frozen.** Whatever was resolved is baked into the rendered manifest and into the manifest stored with that revision. When the upstream address changes, the release does not notice; it keeps the value it captured, until someone happens to run an upgrade, at which point it silently captures a new one. ### What to do instead Resolve at runtime, where resolution belongs. A pod resolving a name through the cluster's DNS gets the current answer, follows changes, and behaves identically no matter who installed the chart. If the address must be decided outside the cluster — an external broker chosen per environment — make it a value, so it is explicit, reviewable, recorded with the release and visible in `helm get values`. If a chart you did not write depends on `getHostByName`, treat that as a portability defect worth patching rather than a flag to turn on globally in your pipeline. ### Where it legitimately appears There is a narrow case: a template that needs a literal IP address, for a field that will not accept a name, in an environment where the rendering host and the cluster share the same resolver view and the address is genuinely stable. That is a real situation, and it is why the capability exists at all. It is worth knowing the flag and the empty-string behaviour, but nobody's offer turns on it — the reason to know it is that the empty string gives no clue whatsoever when you meet it in someone else's chart.
- With `--enable-dns` passed, the render works on a laptop and comes back empty in CI. Why?The lookup runs wherever `helm` runs, using that host's resolver and network. The CI runner may sit in a network with no route to the name, a different resolver, or restricted egress. That divergence is the argument against the technique rather than something to fix: the same chart and values are producing different manifests depending on which machine rendered them.
- A chart resolves an address at render time. What breaks when that address changes?Nothing, immediately — which is the problem. The resolved value is frozen in the rendered manifest and in the manifest stored with that revision, so the release keeps pointing at the old address until someone runs an upgrade, at which point it silently captures whatever resolves then. Resolving in the workload at runtime, or passing the address in as a value, both make the change visible.
It is like writing today's traffic conditions into a printed map. The map is not wrong when it is printed, but it never updates, and a map printed in another city says something else entirely.
saying these in an interview costs you the question
- Reading an empty result as proof the name does not exist
- Assuming the lookup uses the cluster's DNS
- Thinking resolution happens at apply time, not render time
- Expecting an error rather than an empty string
- Treating --enable-dns as a fix rather than a trade-off
- Believing the resolved address updates on its own