Managing Multi-Chart Kubernetes Deployments
Once you’ve figured out how to use individual Helm charts (see Using Existing Helm Charts if you haven’t), you quickly run into the next problem: you need to deploy more than one chart. Your observability stack needs Prometheus, Grafana, and Loki. You need ingress controllers, cert-manager, databases, and your applications. Each has its own Helm chart.
Running helm install ten times manually gets tedious fast. So how do you manage all of these together?
This post shares what I’ve learned about different approaches to multi-chart deployments and when each one makes sense.
This post is for: Platform engineers and DevOps practitioners managing Kubernetes infrastructure with multiple Helm charts who want to understand the trade-offs between different orchestration approaches. The article assumes you understand how to use individual Helm charts. If you’re looking for guidance on customizing single charts (copying vs. dependencies), see Using Existing Helm Charts: Copy, Install, or Dependencies?
TL;DR
Umbrella charts bundle multiple charts into a single deployable unit. They’re useful when components genuinely need coordinated deployment, but dangerous when used just to reduce the number of helm commands you run.
Custom deployment scripts provide explicit control with maximum visibility. Write a bash script that runs helm commands for each chart. Simple and effective for small teams.
Helmfile offers declarative configuration for multiple charts with environment management and dependency ordering. Good middle ground between scripts and full GitOps.
GitOps tools (ArgoCD/Flux) automatically deploy charts based on Git repository state. Best for larger teams with compliance requirements and multi-cluster deployments.
Umbrella Charts: Bundling Dependencies
Umbrella charts take dependencies one step further: they bundle multiple charts into a single deployable unit. The umbrella chart itself typically has no templates, just dependencies and values that configure them.
Here’s a simplified example of an observability stack:
apiVersion: v2name: observability-stackversion: 1.0.0dependencies: - name: prometheus version: 27.45.0 repository: https://prometheus-community.github.io/helm-charts - name: grafana version: 10.1.5 repository: https://grafana.github.io/helm-charts - name: loki version: 6.46.0 repository: https://grafana.github.io/helm-chartsThen configure all three components in your values.yaml:
prometheus: server: retention: '30d' alertmanager: enabled: true
grafana: adminPassword: 'changeme' datasources: datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://prometheus-server
loki: persistence: enabled: true size: 10GiWith a single command, you deploy all three components:
helm dependency update observability-stack/helm install monitoring observability-stack/When Umbrella Charts Make Sense
Umbrella charts work well when components genuinely need coordinated deployment:
1. Tightly coupled components
If application A depends on database B being deployed first, and both are managed via Helm, bundling them ensures proper deployment order.
2. Versioning guarantees
When you’ve tested specific version combinations and need to ensure those exact versions deploy together. An umbrella chart locks the compatible versions.
3. Simplified distribution
Publishing a single chart that deploys a complete stack makes it easier for users to get started. They don’t need to understand which components to install or in what order.
When Umbrella Charts Become Problems
The trouble starts when you use umbrella charts for the wrong reasons.
Anti-Pattern: Reducing Helm Commands
The temptation is strong: instead of running helm install five times for five charts, bundle them into an umbrella chart and run it once. This seems like good automation.
But you’ve just coupled five independent update cycles. Now you can’t upgrade component A without considering components B, C, D, and E. Every update requires rolling out the entire stack.
Real-World Example: Grafana’s LGTM Stack
Grafana’s lgtm-distributed umbrella chart bundles the full observability stack:
- Loki (logs)
- Grafana (visualization dashboards)
- Tempo (traces)
- Mimir (metrics)
This seems convenient: deploy complete observability in one command. But the chart fell behind and became problematic:
The problem: When Loki upgraded from version 2.9.x to 3.x, it brought significant improvements:
- New TSDB storage format (TSDB v13)
- Native OTLP support via
/otlp/v1/logsendpoint - Structured metadata support
But the lgtm-distributed umbrella chart couldn’t just update its Loki dependency.
The umbrella chart locks all component versions together in its Chart.yaml. To upgrade Loki from 2.9.x to 3.x, the chart maintainers needed to:
- Test Loki 3.x compatibility with the current versions of Grafana, Tempo, and Mimir
- Potentially upgrade those components too if incompatibilities surfaced
- Update documentation for the entire stack
- Release a new version of the umbrella chart
This coordination overhead meant the chart fell behind. As reported in GitHub issue #3885, it remained stuck on the old loki-distributed chart (Loki 2.9.x) instead of the newer grafana/loki chart, even though Loki 3.x had been available for months.
Users who wanted Loki 3.x features faced a choice: wait for the umbrella chart to update (unknown timeline), or deploy Loki separately (losing the convenience of the umbrella chart).
The Rule of Thumb
Do these components need to deploy jointly, or can they update independently?
- Deploy jointly → Umbrella chart might make sense
- Update independently → Use separate Helm releases
Most components can update independently. Your application can upgrade separately from your database. Loki can upgrade separately from Grafana. Bundling them creates artificial coupling.
Pros and Cons Summary
Pros of umbrella charts:
- Single command deploys multiple components
- Locks tested version combinations (the chart maintainers verify compatibility so you don’t have to)
- Simplifies distribution for end users
- Ensures deployment order for coupled components
Cons of umbrella charts:
- Couples independent update cycles
- Can’t upgrade one component without updating the entire stack
- Complexity multiplies when one component makes breaking changes
- Hides the actual architecture behind abstraction
Alternatives for Multi-Chart Deployments
Running multiple helm install commands manually gets tedious, but umbrella charts aren’t the solution when components should update independently. The real problem isn’t the number of commands, it’s the lack of automation and reproducibility.
Here are three better approaches that let you manage multiple Helm charts while preserving independent update cycles.
1. Custom Deployment Scripts
Write a simple shell script that runs helm install for each component:
#!/bin/bashset -e
ENV=${1:-dev} # Default to dev if no environment specified
# Deploy infrastructure chartshelm upgrade --install prometheus prometheus-community/prometheus \ -f charts/prometheus/values.yaml \ -f charts/prometheus/values-${ENV}.yaml
helm upgrade --install grafana grafana/grafana \ -f charts/grafana/values.yaml \ -f charts/grafana/values-${ENV}.yaml
helm upgrade --install loki grafana/loki \ -f charts/loki/values.yaml \ -f charts/loki/values-${ENV}.yaml
# Deploy application chartshelm upgrade --install api ./charts/api \ -f charts/api/values-${ENV}.yaml
helm upgrade --install worker ./charts/worker \ -f charts/worker/values-${ENV}.yamlDeploy to different environments:
./deploy.sh dev # Deploy to dev./deploy.sh prod # Deploy to prodBenefits:
- Clear and explicit: anyone can read the script and understand what gets deployed
- Independent updates: upgrade Loki without touching Prometheus
- Simple to modify: add conditional logic, pre/post-deploy hooks, or validation steps
- Environment support: use different values files per environment (values.yaml for base, values-dev.yaml/values-prod.yaml for overrides)
- No new tools: just bash and helm
When to use: Small teams, simple deployments, or when you want maximum visibility into deployment steps.
2. Helmfile
Helmfile is a declarative spec for deploying Helm charts. You define your releases in YAML, and Helmfile handles the execution:
---environments: dev: values: - environment: dev prod: values: - environment: prod---releases: - name: loki namespace: monitoring chart: ./charts/loki values: - charts/loki/values.yaml - charts/loki/values-{{ .Environment.Name }}.yaml labels: collection: 'monitoring'
- name: grafana namespace: monitoring chart: ./charts/grafana values: - charts/grafana/values.yaml - charts/grafana/values-{{ .Environment.Name }}.yaml labels: collection: 'monitoring' needs: - lokiDeploy to different environments:
helmfile -e dev sync # Deploy to devhelmfile -e prod sync # Deploy to prod
# Deploy only specific releaseshelmfile -e dev -l collection=monitoring sync # Deploy only monitoring stackhelmfile -e dev -l name=grafana sync # Deploy only grafanaBenefits:
- Declarative: deployment state is defined in version-controlled YAML
- Environment support: built-in environment management with variables and environment-specific values files
- Works with dependency-based charts: deploy your custom charts that use Helm dependencies
- Dependency management: control deployment order with
needsdeclarations
When to use: Medium-sized teams managing multiple environments, when you want declarative configuration but aren’t ready for GitOps.
3. GitOps: ArgoCD and Flux
GitOps tools automatically deploy Helm charts based on Git repository state. You commit chart definitions to Git, and the tool ensures your cluster matches that state.
ArgoCD provides a UI-driven GitOps experience:
- Web dashboard showing deployment status
- Manual or automatic sync from Git
- RBAC for controlling who can deploy what
- Multi-cluster support
Flux is a GitOps operator that runs in-cluster:
- Completely automated, no UI
- Monitors Git repositories for changes
- Automatically deploys updates when you push commits
- Built-in image automation for updating container tags
Both tools let you define Helm releases in Git:
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: my-argo-workflows namespace: argocdspec: project: default source: repoURL: https://github.com/your-org/your-repo targetRevision: main path: charts/my-argo-workflows helm: valueFiles: - values-prod.yaml destination: server: https://kubernetes.default.svc namespace: workflows syncPolicy: automated: prune: true selfHeal: trueBenefits:
- Git as source of truth: audit trail for all deployments
- Automatic drift detection: if someone manually changes the cluster, GitOps tools revert to Git state
- Pull-based deployments: no need to give CI/CD write access to clusters
- Multi-cluster management: deploy to many clusters from one Git repository
When to use: Larger teams with compliance requirements, multiple clusters, or when you want fully automated deployments with audit trails.
Choosing the Right Approach
Here’s a decision framework for managing multiple Helm charts:
| Your Situation | Recommended Approach |
|---|---|
| Simple deployment, small team, maximum visibility | Custom deployment script |
| Multiple environments (dev/staging/prod) | Helmfile |
| Compliance requirements, audit trails, or multi-cluster | ArgoCD or Flux (GitOps) |
Start simple. Don’t adopt GitOps just because it’s trendy. A 50-line bash script might serve you better than a complex tool you don’t need yet. Add complexity only when you feel the pain it solves.
Key Takeaways
Use Helm dependencies instead of copying charts. Your values.yaml becomes documentation of your changes, and upgrades become trivial version bumps.
Umbrella charts create coupling. Only bundle components that genuinely need coordinated deployment. Independent components should be independent releases.
Better alternatives exist for multi-chart deployments. Deployment scripts, Helmfile, and GitOps tools let you manage multiple charts while preserving independent update cycles.
Resources:
- Helm Dependencies Documentation: https://helm.sh/docs/helm/helm_dependency/
- Helmfile Project: https://github.com/helmfile/helmfile
- ArgoCD Documentation: https://argo-cd.readthedocs.io/
- Flux Documentation: https://fluxcd.io/docs/
- Grafana LGTM Stack Issue: https://github.com/grafana/helm-charts/issues/3885
This article reflects patterns I learned managing two bare-metal Kubernetes clusters at the Professorship of Open Source Software. The goal of this article is to help you avoid common pitfalls and choose the right tool for your specific deployment needs.