Intermediate Lesson 7 of 14

Dependencies & Subcharts

Include third-party charts (PostgreSQL, Redis, etc.) as dependencies, manage subcharts, and use global values.

🧒 Simple Explanation (ELI5)

Imagine building a house. You don't manufacture the plumbing, electrical wiring, and appliances yourself — you buy them from specialists and integrate them. Helm dependencies work the same way: your app chart imports pre-built charts (PostgreSQL, Redis, NGINX) and configures them through values.

🔧 Technical Explanation

Declaring Dependencies

yaml
# Chart.yaml
apiVersion: v2
name: myapp
version: 1.0.0
dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled    # Toggle on/off
  - name: redis
    version: "17.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
  - name: common
    version: "2.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    tags:
      - infrastructure

Dependency Commands

bash
# Download dependencies (creates charts/ dir with .tgz files)
helm dependency update ./myapp

# List dependencies and their status
helm dependency list ./myapp

# Rebuild from Chart.lock (reproducible builds)
helm dependency build ./myapp
Dependency Resolution
Parent Chart
myapp
depends on →
Subcharts (in charts/)
postgresql-12.x.x.tgz
redis-17.x.x.tgz

Configuring Subchart Values

yaml
# values.yaml of parent chart
# Subchart values go under a key matching the subchart name

postgresql:
  enabled: true
  auth:
    postgresPassword: "secretpass"
    database: myappdb
  primary:
    resources:
      requests:
        cpu: 250m
        memory: 256Mi

redis:
  enabled: true
  architecture: standalone
  auth:
    enabled: false

Global Values

yaml
# values.yaml
global:
  imageRegistry: myregistry.azurecr.io
  storageClass: managed-premium

# In ANY template (parent or subchart):
image: {{ .Values.global.imageRegistry }}/myapp:v1
# Both parent and subcharts can access .Values.global
💡
Global vs Subchart Values

global. values are passed to all subcharts automatically. Non-global subchart values must be nested under the subchart's name key. Example: postgresql.auth.database configures the postgresql subchart's auth.database value.

condition vs tags

Featureconditiontags
ScopeSingle dependencyGroup of dependencies
Howcondition: postgresql.enabledtags: [backend]
Override--set postgresql.enabled=false--set tags.backend=false
Prioritycondition overrides tagstags are checked if condition is absent

Chart.lock

Chart.lock is auto-generated by helm dependency update. It records exact resolved versions (like package-lock.json). Use helm dependency build in CI/CD to get the exact same versions without re-resolving.

Chart.lock Workflow in Practice

bash
# Developer workflow:
# 1. Change a dependency version in Chart.yaml
vim Chart.yaml   # Update postgresql from 12.x.x to 13.x.x

# 2. Re-resolve dependencies (updates Chart.lock + downloads .tgz)
helm dependency update .

# 3. COMMIT BOTH Chart.yaml AND Chart.lock to Git
git add Chart.yaml Chart.lock
git commit -m "chore: upgrade postgresql subchart to 13.x.x"

# CI/CD workflow (reproducible):
# Use 'build' (not 'update') to install from the lockfile exactly
helm dependency build .
# ↑ This uses Chart.lock to get the EXACT same versions
# If Chart.lock is missing or out of date, 'build' fails
Always Commit Chart.lock

Just like you commit package-lock.json, always commit Chart.lock. Without it, helm dependency update in CI might resolve a different version than what you tested locally (e.g., if a new patch was published). This can introduce bugs silently.

Version Pinning Strategies

StrategyChart.yamlRiskWhen to Use
Exact pinversion: "12.12.10"Miss security patchesProduction charts, strict reproducibility
Patch rangeversion: "12.12.x"Low — patches onlyBalanced: get fixes, avoid breaking changes
Minor rangeversion: "12.x.x"Medium — new features possibleDevelopment, internal charts
Major rangeversion: ">=12.0.0"High — breaking changes possibleAlmost never in production

⌨️ Hands-on

bash
# Create a chart with a PostgreSQL dependency
helm create depslab
cd depslab

# Add dependency to Chart.yaml
cat >> Chart.yaml <<EOF

dependencies:
  - name: postgresql
    version: "12.12.10"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
EOF

# Add subchart values to values.yaml
cat >> values.yaml <<EOF

postgresql:
  enabled: true
  auth:
    postgresPassword: "demopass"
    database: depslab_db
EOF

# Download the dependency
helm dependency update .

# Verify
ls charts/   # Should show postgresql-12.12.10.tgz
helm dependency list .

# Render to see all resources (parent + subchart)
helm template test . | grep "kind:" | sort | uniq -c

# Install with dependency
helm install depslab . -n demo --create-namespace

# Disable the dependency
helm upgrade depslab . -n demo --set postgresql.enabled=false

# Cleanup
helm uninstall depslab -n demo

🐛 Debugging Scenarios

Scenario 1: "Error: no repository definition for bitnami"

bash
# You need to add the repo first
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Then update dependencies
helm dependency update ./mychart

Scenario 2: Subchart values not being applied

bash
# Values for subcharts must be nested under the subchart name
# WRONG:
auth:
  database: mydb   # Parent chart's auth, NOT postgresql's

# RIGHT:
postgresql:
  auth:
    database: mydb  # Goes to postgresql subchart

# Verify what gets passed:
helm template test . | grep -A 10 "kind: Secret"

Scenario 3: Version conflict

bash
# "Error: chart requires postgresql >= 12.0.0, found 11.9.0"

# Check current lock:
cat Chart.lock

# Fix: Update the version range in Chart.yaml
# Then re-resolve:
helm dependency update .

Scenario 4: Subchart upgrade introduced breaking changes

bash
# After running helm dependency update, your chart breaks
# because the subchart changed its values schema

# Step 1: Check what version changed
diff <(git show HEAD:Chart.lock) Chart.lock
# Shows: postgresql went from 12.12.10 to 13.1.0

# Step 2: Check the subchart's changelog/release notes
helm show chart bitnami/postgresql --version 13.1.0

# Step 3: Rollback — restore the old lockfile
git checkout Chart.lock
helm dependency build .   # Reinstall old versions

# Step 4: Fix forward (when ready)
# Read migration guide, update values.yaml for new schema
# Example: auth.postgresPassword → auth.password (Bitnami change)
# Test: helm template test . | grep postgres
# Then: helm dependency update . → commit both files
🔗
K8s Connection: Dependencies Deploy Real K8s Resources

Each subchart creates its own Kubernetes resources (Deployments, Services, PVCs). When you helm install a chart with PostgreSQL as a dependency, you get a full PostgreSQL StatefulSet, Service, and PersistentVolumeClaim in your namespace. Run kubectl get all -n <ns> after install to see everything — not just your app's pods. Understanding this is critical for debugging: if your app can't connect to the database, check the subchart's pods and services, not just your own.

🎯 Interview Questions

Beginner

Q: What is a Helm subchart?

A subchart is a chart included as a dependency of another chart. It lives in the parent chart's charts/ directory (as a .tgz archive). Example: your app chart depends on a PostgreSQL subchart. When you install the parent, both the app and PostgreSQL are deployed together.

Q: How do you declare a dependency?

In Chart.yaml under the dependencies: key. Specify name, version, and repository. Then run helm dependency update to download the subchart into charts/. Use condition to make it optional.

Q: What is Chart.lock?

Auto-generated lockfile that records the exact resolved versions of all dependencies. Like package-lock.json in Node.js. Use helm dependency build to install from the lockfile (reproducible). helm dependency update re-resolves and updates the lockfile.

Q: How do you pass values to a subchart?

Nest values under a key matching the subchart name. If subchart is "postgresql", put values under postgresql: in the parent's values.yaml. Example: postgresql.auth.database: mydb. Global values (global:) are automatically available in all subcharts.

Q: What are global values?

Values under the global: key are shared with all subcharts. Accessible via .Values.global.key in any template (parent or subchart). Used for cross-cutting concerns: image registry, storage class, domain name.

Intermediate

Q: What is the difference between condition and tags for dependencies?

condition toggles a single dependency: condition: postgresql.enabled. tags groups multiple dependencies: tags: [backend] — all dependencies with the "backend" tag are toggled together. Condition takes priority over tags if both are set.

Q: What is an umbrella chart?

A chart with no templates of its own, only dependencies. Used to deploy a complete application stack (app + database + cache + monitoring) with one helm install. The umbrella chart's values.yaml configures all subcharts. Common in microservices architectures for deploying an entire environment.

Q: What is a library chart?

A chart with type: library in Chart.yaml. It contains only helper templates (_helpers.tpl), no manifest templates. Cannot be installed directly — used as a dependency to share common template patterns. Example: Bitnami's "common" chart provides shared label generators, name truncation, etc.

Q: Can a subchart override parent values?

No. Values flow top-down: parent chart's values override subchart defaults. A subchart cannot access or modify the parent's values. It can only access its own values and .Values.global. This is by design — the parent is in control.

Q: What happens if you forget to run 'helm dependency update'?

helm install will fail with "found in Chart.yaml, but missing in charts/ directory". Always run helm dependency update (or build) before install/package. In CI/CD, include it as a build step.

Scenario-Based

Q: Your app needs PostgreSQL in all environments but Redis only in production. How?

In Chart.yaml: both as dependencies with conditions. condition: postgresql.enabled and condition: redis.enabled. In values-dev.yaml: redis.enabled: false. In values-prod.yaml: redis.enabled: true. PostgreSQL is enabled by default in values.yaml.

Q: Two subcharts need the same image registry. How do you avoid configuring it twice?

Use global values: global.imageRegistry: myregistry.azurecr.io. Both subcharts access it via .Values.global.imageRegistry. This is the exact use case globals are designed for — cross-cutting configuration shared across all charts in the dependency tree.

Q: Your team maintains 10 microservices with identical Deployment/Service templates. How do you reduce duplication?

Create a library chart with shared named templates. Each microservice chart depends on the library chart and uses {{ include "baselib.deployment" . }}. Or create a base application chart that all services use as a dependency, overriding only the values. Publish to an internal chart repo.

Q: A subchart upgrade introduced a breaking change. Your helm upgrade fails. How do you handle it?

1) Rollback: helm rollback <release>. 2) Pin the subchart version in Chart.yaml to the last working version. 3) Run helm dependency update. 4) Read the subchart's changelog for migration instructions. 5) Update your values to match the new schema. 6) Test with helm template and helm upgrade --dry-run. 7) Use Chart.lock for reproducible builds.

Q: You want to deploy your entire microservices stack with one command. How?

Create an umbrella chart with all services as dependencies (each as a subchart). Each subchart has its own templates. The umbrella's values.yaml configures all services. helm install mystack ./umbrella -f values-prod.yaml deploys everything. Alternative: use Helmfile for multi-release management without umbrella chart complexity.

🌍 Real-World Use Case

A data analytics platform uses an umbrella chart for its full stack:

📝 Summary

← Back to Helm Course