TT Lab
Get started
Learn Learning paths Courses

Authoring and Shipping Helm Charts

Dependencies and Hooks — Assembly and Ordering

Continue in TT Lab

Summary in one line

Dependencies are the grammar for assembling charts, and hooks are the grammar for deciding "what to do first" inside that assembled deployment.

Why this is needed

Suppose a service now needs a cache. Putting the cache manifests right into the same chart is convenient for the moment. But the instant a second service wants the same cache, copying begins, and when the cache configuration needs fixing, nobody knows how many places have to be edited. Conversely, if you split the cache off completely as a separate release, people then have to remember the order "the cache must exist before the app is deployed."

Dependencies are a compromise between the two. You keep the cache as an independent chart, but the parent chart pulls it in declaratively. The parent can override the child's values with its own values, and can turn off the child entirely with a single condition. Yet the child chart still remains something that can be installed on its own.

How it works

You write dependencies in the dependencies list of the parent's Chart.yaml. Each entry has name, version, and repository, and optionally condition and tags. A repository can be a remote URL, but it can also be a local path starting with file://. A local path is interpreted relative to the parent chart directory, and is especially useful in environments with no internet access or in structures where charts are kept together in one repository.

When you run helm dependency update, two things happen. The dependency chart is placed in charts/ in packaged form, and Chart.lock is created. The lock file holds the actually resolved versions and a digest, and this digest serves as a fingerprint of "the declared list of dependencies." That is why in CI you use helm dependency build rather than update. build reproduces exactly what is written in the lock file, and fails on the spot if the declaration changed but the lock file did not. This distinction is exactly the same idea as a lock file in the application world.

The rule for passing values to a subchart is simple. What you write in the parent values under a key with the same name as the subchart becomes the top level of the child's .Values. If you write replicaCount: 3 under cache:, the child sees it as its own .Values.replicaCount. The point is that the parent overrides it while the child chart's values.yaml is left untouched. Conversely, values that must reach both the parent and the child go under global. Values shared across the whole system, such as an image registry address or an environment name, belong there.

There are two ways to turn things on and off. condition activates the child only when a certain value is true, and tags handles several children grouped by a tag at once. If the two conflict, condition wins.

Hooks are different in nature. A hook is a resource that steps in at a specific point in the release lifecycle, and it is declared with annotations.

Annotation Meaning
helm.sh/hook When to run it (pre-install, post-install, pre-upgrade, pre-rollback, etc.)
helm.sh/hook-weight The order among hooks at the same point. The smaller value goes first
helm.sh/hook-delete-policy When to clean it up (before-hook-creation, hook-succeeded, hook-failed)

The most frequently mistaken thing here is the deletion policy. A hook resource is not owned by the release. So if you do not write a policy, completed Jobs pile up in the namespace on every upgrade, and the next hook collides when it tries to be created with the same name.

What it looks like in the field

First, what remains after you turned it off. If you turned off a subchart with a condition but its name still appears in the rendering, it means the parent chart is directly creating child-related resources in its own templates. A condition turns off only the child chart, not the parent's templates.

Second, the cost of umbrellas. Bundling several services into one umbrella chart is convenient because they deploy together, but even to fix one service you must deploy the whole thing. The blast radius of a single release's failure grows by that much. If independent deployment matters, it is better to keep them as subcharts and release each on its own.

Third, hooks are not rolled back. Even if you ran a migration Job as a hook and the deployment later failed and was rolled back, the schema does not go back. The work done in hooks should preferably be reversible or safe to run several times.

What you will do in the next lab

You create a /root/helm/deps/cache subchart and a /root/helm/deps/platform parent chart and connect them with a file:// local repository. You confirm that the lock file is created, override the child's values from the parent, and switch the child off and on with a condition. You confirm with labels that a global value reaches both sides, and finally attach a hook Job and produce an umbrella rendering report.