skip to content

What does an import-values entry on a Helm chart's dependency actually do?

level: seniorimportance: nice to knowfreq 16%

answer

  1. The only child-to-parent value flow
  2. Declared on a dependency, not in a template
  3. Two forms: exports, or path mapping
  4. The child publishes under exports
  5. Imported keys land in the parent's values

basics

~20 s

import-values, listed on a dependency in Chart.yaml, copies values upward from the subchart into the parent's values — either a block the child publishes under exports, or an explicit child path mapped to a parent path.

solid answer

~50 s

It is the only value flow that runs child-to-parent. Listed on a dependency entry in the parent's `Chart.yaml`, `import-values` takes two forms. Given a bare string, Helm looks in the subchart's values for `exports.<string>` and copies the contents of that block into the **parent's** values at the top level — the child publishes an explicit export block, the parent imports it by name. Given a `child`/`parent` pair, Helm copies the value at the child's dotted path to the given path in the parent's values, with no cooperation needed from the child. Charts use it so a parent's own templates can reuse a default the bundled subchart already defines — a port, a service name, a bucket prefix — instead of restating it and letting the two drift. The cost is coupling: the parent's values now depend on the child's internal layout, so bumping the subchart can silently move the import. Verify with a client-side dry run.

code

yaml · 5 lines
yaml
# charts/queue/values.yaml
exports:
  queueDefaults:
    port: 6414
    protocol: amqp

go deeper

for a junior

You are unlikely to be asked this. Just recognise the name and its direction: import-values sits on a dependency in Chart.yaml and moves values from a subchart up into the parent, not the other way round.

for a middle

Be able to name both forms — a child's exports block imported by name, and an explicit child-path-to-parent-path mapping — and say where the imported keys end up in the parent's values.

for a senior

Show the judgement: imports remove drift between a parent and a bundled subchart, at the price of a hidden dependency on the child's internals that a version bump breaks silently. Say how you would test for that.

for a principal

Weigh the readability cost. A value that appears in the computed tree but nowhere in the parent's values file is a maintenance trap; decide whether your teams get a shared namespace, an explicit duplication, or this.

### The one value flow that goes upward Everything else about umbrella charts pushes values down: the parent writes a block named for a dependency, and the subchart reads it. `import-values` is the exception. It is a field on a dependency entry in the parent's `Chart.yaml`, and it copies values *out of* the subchart and *into* the parent's own values, before either chart renders. It exists to solve a real duplication problem. Suppose the umbrella bundles a queue subchart that defaults its listening port to `6414`, and the parent itself renders an object that has to point at that port. Without imports the parent restates `6414` in its own values, and the day the subchart changes its default the two disagree — with nothing failing loudly, because both files are individually valid YAML. ### Form one: the exports contract When `import-values` is given a plain string, Helm looks for `exports.<string>` in the subchart's values and copies **the contents of that block** into the parent's values at the top level. ```yaml # charts/queue/values.yaml (the child) exports: queueDefaults: port: 6414 protocol: amqp ``` ```yaml # transcode-platform/Chart.yaml (the parent) dependencies: - name: queue version: 3.2.7 repository: oci://registry.example.internal/charts import-values: - queueDefaults ``` The parent's templates now read `.Values.port` and `.Values.protocol` — note that the keys land at the parent's root, not under a `queueDefaults` key. This form is a deliberate contract: the child author decided what is publishable by putting it under `exports`, and the parent asks for it by that name. ### Form two: the child/parent mapping The second form needs no cooperation from the child at all. You name a dotted path inside the child's values and a path in the parent's values to copy it to: ```yaml dependencies: - name: queue version: 3.2.7 repository: oci://registry.example.internal/charts import-values: - child: service.port parent: queueImports.port ``` After that the parent reads `.Values.queueImports.port`. Because it reaches into arbitrary internals, this form is the one that ages badly: `service.port` is a key the child never promised to keep, and a minor subchart bump can move or rename it. Nothing errors — the import simply lands nothing, and the parent renders whatever its own default was, or an empty field. ### Where it sits in the pipeline Imports are resolved as part of dependency processing, before templates run, so an imported value is an ordinary value by the time anything renders: it appears in the computed values tree, it can be read with `.Values`, and it participates in the same coalescing as everything else. That also means it is invisible in the source: a reader of the parent's `values.yaml` sees no sign of `port` at all, which is the main readability objection to the feature. Anyone maintaining the chart has to know to look in `Chart.yaml`. Because of that indirection, always confirm what actually arrived rather than reasoning about it: ```bash helm install transcode ./transcode-platform --dry-run=client --debug ``` The computed values block in that output is the ground truth for whether the import fired and where it landed. Do not assume an import will pick up an override a caller supplied for the child — check the rendered output for the case you care about instead of trusting a mental model of the ordering. ### Is it worth using? Honestly, most production umbrella charts never touch it, and an interview will treat it as a differentiator rather than a requirement. It earns its place in one situation: a parent that must render resources agreeing with a bundled subchart's own defaults, where restating those defaults would create a silent drift risk. Prefer the `exports` form when you control the child, because it is an explicit published surface rather than a reach into internals. The alternatives are usually simpler and are what most teams pick. Put the shared fact in `global` so both charts read it from one place. Or state it once in the parent and pass it down into the subchart's block explicitly, which reads plainly in the values file. Both keep the value visible where a maintainer will look for it; `import-values` trades that visibility for the guarantee that the two sides cannot drift. ### How to review a chart that uses it When you inherit an umbrella chart, read `Chart.yaml` before `values.yaml`. An `import-values` entry means some of the parent's values have no visible source, and a reviewer who only reads the values file will conclude a key is unset when it is in fact supplied by a subchart. Leave a comment in the parent's values file naming the imported keys and the dependency they come from, even though the keys themselves are absent — that one comment is the difference between a maintainable import and a trap. And treat every dependency version bump as a change to the import: render the chart, diff the manifest, and assert on the field the import feeds, because a moved child path breaks the import silently rather than loudly.

  • What is the difference between the string form of import-values and the child/parent form?
    The string form is a contract: the child publishes a block under `exports.<name>` and the parent imports it by that name, landing its contents at the parent's root. The child/parent form maps any dotted path in the child's values to any path in the parent's, needing no cooperation from the child — which also means nothing protects it when the child's internals change.
  • An import stopped working after bumping the subchart from 3.2.7 to 3.4.0. What happened, and how would you have caught it?
    The child path the import reads was almost certainly renamed or moved; imports fail silently, leaving the parent on its own default. Catch it by rendering the chart in CI after every dependency bump and asserting on the field that depends on the imported value, rather than trusting that a passing `helm lint` means the values still line up.
  • When would you use a global instead of import-values?
    When the fact belongs to the installation rather than to the subchart — a registry mirror, a storage class, a domain. Globals flow downward from one visible place in the parent's values, which is easier to read and maintain. Reach for imports only when the authoritative value genuinely lives in the child and duplicating it would let the two drift.

saying these in an interview costs you the question

  • Thinks import-values pushes parent values into a subchart
  • Expects the string form to require no exports block
  • Believes imported keys land under the dependency's name
  • Assumes a failed import raises an error
  • Confuses it with the global block's downward propagation

context