Helm’s --dry-run and --debug flags are your best friends for understanding what Helm would do without actually doing it, and then seeing why it’s doing it.
Let’s see it in action. Imagine you have a chart named my-app in a local directory.
helm install my-app ./my-app --dry-run --debug
This command won’t actually install anything on your cluster. Instead, it will render all the Kubernetes manifests that Helm would generate based on your chart’s templates and values, and then it will print them to your terminal, along with extensive debugging information.
The core problem Helm solves is managing complex, multi-component applications on Kubernetes. A single application might consist of Deployments, Services, ConfigMaps, Secrets, Ingresses, and more. Manually creating and managing all these YAML files is tedious and error-prone. Helm introduces the concept of "charts," which are packages of pre-configured Kubernetes resources. You can then customize these charts using values.yaml files.
When you run helm install (or upgrade), Helm takes your chart, applies your custom values, and uses the Go templating engine to render the final Kubernetes manifests. The --dry-run flag tells Helm to perform this rendering process but skip the API calls to your Kubernetes cluster. The --debug flag, when used with --dry-run, adds a wealth of information to the output. This includes:
- Rendered Manifests: The complete, final YAML output for every Kubernetes resource that would be created.
- Template Debugging: Information about which template files were processed and how they were rendered.
- Value Dump: A clear view of all the values Helm is using, including defaults, values from your
values.yaml, and any overrides provided on the command line. This is crucial for understanding why a particular value is being used. - Dependencies: If your chart has dependencies,
--debugwill show how those are being resolved and rendered.
Here’s what a snippet of the output might look like (simplified):
# Source: my-app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-release-name
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: release-name
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: release-name
template:
metadata:
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: release-name
spec:
containers:
- name: my-app
image: "nginx:1.16.0" # This image might be coming from your values.yaml
ports:
- containerPort: 80
---
# Source: my-app/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-release-name
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: release-name
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: release-name
And interspersed with this YAML, you’ll see lines like:
helm.sh/hook: pre-install
...
NAME: my-app-release-name
LAST DEPLOYED: Mon Oct 26 10:00:00 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running:
printf "http://%s.%s.nip.io\n" my-app-release-name my-app-release-name.default.svc.cluster.local
The --debug flag is particularly useful for troubleshooting templating issues. If a value isn’t rendering as you expect, --debug will show you the exact values Helm is using and the output of the template functions. You can even use {{ .Debug.Dump }} within your templates to dump the entire context at that point in the rendering process, which is invaluable for complex templates.
The true power of --dry-run --debug is that it allows you to simulate an installation or upgrade without impacting your cluster. You can catch syntax errors in your templates, logic errors in your conditional statements, or incorrect value substitutions before they cause a real deployment failure. It’s a critical step in a robust CI/CD pipeline for Kubernetes applications managed by Helm.
The most surprising thing is how often people miss the --debug part and just use --dry-run, which gives you the YAML but not the why behind the rendering. The debug output shows you the exact values that were injected into your templates, which is often the missing piece when a template doesn’t behave as expected. It reveals the effective values after all overrides and defaults have been applied, presenting a unified view that you can’t get from just looking at your local values.yaml files.
Once you’re confident with the --dry-run output, the next step is to actually deploy or upgrade your application.