skip to content

A bundled Helm subchart still renders even though you set enabled: false. Where must that value actually live?

level: middleimportance: should knowfreq 45%

answer

  1. The lookup starts at the root
  2. Not the packaged subchart's own defaults
  3. An alias renames the key
  4. A grandchild needs the full path
  5. Render locally before blaming the chart

basics

~20 s

A dependency's condition path is looked up in the values the release is rendered with, addressed from the top of that tree. Setting enabled: false inside the subchart's own values.yaml, or under the wrong key, leaves the path unresolved - and an unresolved path is ignored silently, so the subchart renders.

solid answer

~50 s

Helm computes dependency enablement from the values it is about to render with, reading the condition path from the **root** of that values tree - typically the parent chart's `values.yaml`, plus whatever `-f` files and `--set` flags the caller supplies. Three things put the value somewhere the condition never looks: writing `enabled: false` in the packaged subchart's own `values.yaml`; putting it under the wrong key, for example the chart's real name when the dependency was pulled in under an `alias`; and addressing a grandchild without the full path from the top, so `db.enabled` instead of `billing.db.enabled`. Because an unresolvable path is skipped without an error, all three look identical: the flag "does nothing". The fix is to set the exact path the entry names, at the top level, and to confirm by rendering the chart locally with the same values before touching a cluster.

code

yaml · 7 lines
yaml
# Chart.yaml of the umbrella chart - the alias decides the key
dependencies:
  - name: postgresql
    alias: billing-db
    version: 15.3.2
    repository: https://charts.example.internal
    condition: billing-db.enabled

go deeper

for a junior

Remember that the switch belongs in the values you pass to the release, at the path the dependency entry names - not inside the subchart's own file.

for a middle

Explain why the failure is silent: an unresolvable path is skipped by design so conditions can list fallbacks, which leaves the dependency at its enabled default.

for a senior

Demonstrate the diagnosis loop - read the condition string from Chart.yaml, render locally with the real values, and check what the live release was actually given - rather than redeploying to test.

for a principal

Own the authoring side: name and default optional switches in the parent's values.yaml, document them, and treat a switch discoverable only from Chart.yaml as a defect in the chart's interface.

### The failure Someone adds `enabled: false`, runs the upgrade, and the bundled component is still there. No error, no warning they noticed, nothing in the output that points at the switch. This is the most common Helm dependency question asked in interviews precisely because the failure is silent: an unresolvable condition path is skipped, and a skipped condition leaves the dependency in its default state, which is enabled. ### Where the lookup happens Before rendering, Helm assembles the values for the release - the parent chart's `values.yaml`, then every `-f`/`--values` file, then `--set` and friends - and evaluates each dependency's `condition` against that single tree, addressed from its root. Everything else is somewhere the lookup does not reach. **Case 1: inside the subchart's own values.yaml.** Editing the vendored chart under `charts/` to say `enabled: false` is the classic mistake. That file supplies defaults for the subchart's own templates; it is not where the parent's dependency switch is read from. It also gets overwritten by the next `helm dependency update`. **Case 2: the wrong key, usually an alias.** When a dependency is declared with `alias: billing-db`, the values namespace that copy reads is `billing-db`, and a chart author will normally point the condition at `billing-db.enabled`. Set `postgresql.enabled: false` instead and you have set a key nothing consults. The rule is simple: the only path that matters is the exact string in the `condition` field of that entry. **Case 3: a grandchild.** In an umbrella chart, a middle chart may itself declare an optional dependency. The values for the whole release are one tree rooted at the top-level chart, so the switch has to be addressed the whole way down: `billing.db.enabled`, not `db.enabled`. Passing the short path from the top leaves it unresolved. **Case 4: types and quoting.** `--set db.enabled="false"` and a quoted `"false"` in YAML are strings. A condition only acts on a real boolean, so a string is ignored - with a warning, which is easy to miss in a busy upgrade log. ### Confirming it, without guessing Rendering locally is the whole answer. Run the chart through the template renderer with the identical values files and `--set` flags and look at what comes out; if the subchart's objects are still there, the switch did not take effect and you can iterate in seconds rather than per deploy. Two more things help: - read the entry in `Chart.yaml` and copy the condition string verbatim - do not infer it from the chart's name; - for a release that already exists, ask Helm what values it was actually given, which distinguishes "my file is wrong" from "my file never reached the release". A rendered diff between the current values and the proposed ones is the fastest way to see both that the subchart disappears and that nothing else moved. ### Why Helm behaves this way The silence is deliberate, not an oversight. A condition may name several paths, and most of them are expected to be missing - that is how a chart offers a specific key with a shared fallback. A missing path therefore cannot be an error; it just means "no opinion here, try the next one". The cost of that design is exactly the failure above, and the mitigation is on the author's side: name the switch clearly, give it a default in the parent's `values.yaml` so it appears in the file everyone reads, and document it. A chart whose optional components are only discoverable by reading `Chart.yaml` is a chart whose consumers will file this bug. ### The neighbouring confusion One more distinction is worth stating plainly, because it produces the same symptom. Setting a value that the subchart's *templates* read - a replica count, an image tag - has nothing to do with enablement; it configures a subchart that is rendering. Enablement is decided before rendering starts, from the condition path alone. Two different mechanisms, one values file, and mixing them up is why people report that "values do not work" when the truth is that one specific path was never set.

  • How do you disable an optional dependency that belongs to a subchart, not to the top-level chart?
    Address it from the root of the release's values with the full path - `billing.db.enabled` for a dependency the `billing` subchart declares - because the whole release renders from one values tree. The short path the middle chart's own Chart.yaml names is relative to that chart, so passing it at the top level resolves to nothing and the dependency stays enabled.
  • Will helm lint or a render catch a misspelled condition path?
    Neither reports the typo directly, because a missing path is legitimate - conditions may list fallbacks that are expected to be absent. What a local render does show is the consequence: the subchart's objects are still in the output. That is why the check is "did the resources disappear", not "did the command complain".
  • Why is editing the vendored chart under charts/ the wrong fix?
    It is not where the switch is read from, and it does not survive: `helm dependency update` replaces the vendored archive, so the edit vanishes on the next refresh and the chart's behaviour silently changes back. Optional components are meant to be controlled from the caller's values, which is reviewable and travels with the release.

saying these in an interview costs you the question

  • Editing the vendored subchart under charts/ to disable it
  • Assuming the key matches the chart name when an alias exists
  • Expecting an error message for an unresolved condition path
  • Setting a grandchild's switch with a short, unrooted path
  • Confusing configuring a subchart with enabling it

context