Chart Dependencies
Manage chart dependencies to compose complex applications. Use subcharts, conditions, and dependency overrides.
Chart Dependencies
In the previous tutorial, we learned about named templates for code reuse within a chart. But what about reusing entire charts? If your app needs Redis, PostgreSQL, or NGINX, you don't have to write those charts yourself. You declare them as dependencies and Helm handles the rest.
Why Dependencies?
Real applications don't run alone. A typical web app might need:
- A database (PostgreSQL)
- A cache (Redis)
- A message queue (RabbitMQ)
- A reverse proxy (NGINX)
Without dependencies, you'd either:
- Cram everything into one massive chart (nightmare to maintain)
- Install each chart separately and wire them together manually (tedious)
Dependencies let you compose a single chart that brings along everything it needs.
Declaring Dependencies
Dependencies live in Chart.yaml:
# Chart.yaml
apiVersion: v2
name: my-web-app
version: 1.0.0
appVersion: "2.0.0"
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
Each dependency specifies:
| Field | Required | Description |
|---|---|---|
name | Yes | Chart name in the repository |
version | Yes | SemVer range to accept |
repository | Yes | Chart repo URL or file:// path |
condition | No | Values path that enables/disables |
tags | No | Group dependencies by tag |
alias | No | Rename the dependency |
import-values | No | Import child values into parent |
Downloading Dependencies
After declaring dependencies, download them:
# Download dependencies into charts/ directory
helm dependency update ./my-web-app
# Or the shorter form
helm dep up ./my-web-app
This creates:
my-web-app/
├── Chart.yaml
├── Chart.lock # Locked versions (like package-lock.json)
├── charts/
│ ├── postgresql-13.2.24.tgz
│ └── redis-18.4.0.tgz
├── values.yaml
└── templates/
The Chart.lock file pins exact versions so builds are reproducible. To rebuild from the lock file (without resolving new versions):
helm dependency build ./my-web-app
Version Ranges
Helm uses SemVer for version constraints:
dependencies:
# Exact version
- name: redis
version: "18.4.0"
# Patch range (18.4.x)
- name: redis
version: "~18.4.0"
# Minor range (18.x.x)
- name: redis
version: "^18.0.0"
# Explicit range
- name: redis
version: ">=18.0.0 <19.0.0"
# Wildcard
- name: redis
version: "18.x.x"
"Which should I use?"
For most cases, "18.x.x" or "^18.0.0" is a good balance — you get bug fixes and minor features but no breaking changes.
Configuring Dependencies
Here's the cool part. You can override dependency values from your parent chart's values.yaml:
# values.yaml of the parent chart
# My app's values
replicaCount: 3
image:
repository: my-web-app
tag: "2.0.0"
# PostgreSQL dependency values (nested under the dependency name)
postgresql:
auth:
username: myapp
password: secret123
database: myapp_db
primary:
persistence:
size: 10Gi
# Redis dependency values
redis:
architecture: standalone
auth:
enabled: false
master:
persistence:
size: 1Gi
The convention: nest dependency configuration under a key matching the dependency name. Helm passes those values to the sub-chart automatically.
Connecting to Dependencies
In your parent chart templates, you can reference the dependency services:
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-config
data:
# PostgreSQL service is named <release>-postgresql by default
DATABASE_URL: "postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ .Release.Name }}-postgresql:5432/{{ .Values.postgresql.auth.database }}"
# Redis service
REDIS_URL: "redis://{{ .Release.Name }}-redis-master:6379"
Conditional Dependencies
Not every environment needs every dependency. Use condition to make dependencies optional:
# Chart.yaml
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
# values.yaml
postgresql:
enabled: true
redis:
enabled: false # Skip Redis in dev
When redis.enabled is false, Helm skips the Redis sub-chart entirely — no Redis resources are created.
In your templates, adapt the configuration accordingly:
{{- if .Values.redis.enabled }}
REDIS_URL: "redis://{{ .Release.Name }}-redis-master:6379"
{{- end }}
Tags — Grouping Dependencies
Tags let you enable/disable groups of dependencies at once:
# Chart.yaml
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
tags:
- database
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
tags:
- cache
- name: prometheus
version: "25.x.x"
repository: https://prometheus-community.github.io/helm-charts
tags:
- monitoring
- name: grafana
version: "7.x.x"
repository: https://grafana.github.io/helm-charts
tags:
- monitoring
# values.yaml
tags:
database: true
cache: true
monitoring: false # Disable both prometheus and grafana
Aliases — Multiple Instances
What if you need two Redis instances? Use alias:
# Chart.yaml
dependencies:
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
alias: redis-cache
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
alias: redis-session
# values.yaml
redis-cache:
architecture: standalone
auth:
enabled: false
redis-session:
architecture: standalone
auth:
enabled: true
password: "session-secret"
Each alias creates a separate instance with independent configuration.
Local Dependencies
During development, you might want to reference a chart on your filesystem instead of a remote repository:
# Chart.yaml
dependencies:
- name: my-common-lib
version: "1.0.0"
repository: "file://../my-common-lib"
The file:// protocol points to a local directory relative to the chart. This is perfect for:
- Monorepos with multiple charts
- Developing a library chart alongside an application chart
- Testing changes before publishing
import-values — Flattening Configuration
By default, dependency values are nested under the dependency name. With import-values, you can import child values into the parent scope:
# Chart.yaml
dependencies:
- name: postgresql
version: "13.x.x"
repository: https://charts.bitnami.com/bitnami
import-values:
- child: auth
parent: database
Now instead of postgresql.auth.username, you can use database.username in your parent values.
Overriding Dependency Templates
Sometimes you need to customize how a dependency behaves. Your options:
- Override values — the preferred way (shown above)
- Post-render — use
--post-rendererto patch output - Fork the chart — last resort (you lose upstream updates)
# Post-render example with kustomize
helm install my-app ./my-chart --post-renderer ./kustomize-renderer.sh
Managing Dependencies Workflow
# 1. Add dependency to Chart.yaml
# 2. Download it
helm dep up ./my-chart
# 3. Check what's in charts/
ls ./my-chart/charts/
# 4. See the dependency tree
helm dep list ./my-chart
# 5. Install everything together
helm install my-release ./my-chart
# 6. When you update dependency versions:
helm dep update ./my-chart
What's Next?
Dependencies let you compose complex applications from reusable building blocks. Combined with conditions and aliases, you have fine-grained control over what gets deployed and how.
In the next tutorial, we'll learn about Helm hooks — special resources that run at specific points in a release lifecycle, like database migrations before an upgrade or cleanup jobs after a delete.