How to use helm lint, helm –debug –dry-run and helm template

In this article we are going to cover How to use Helm lint, helm debug –dry-run and helm template with examples.

Helm, the Kubernetes package manager, empowers developers and operators in streamlining application deployments and managing Kubernetes resources efficiently. Among its arsenal of commands, three stand out as indispensable tools for ensuring smooth deployments and maintaining chart integrity: 'lint‘, ‘template', and '--dry-run

Prerequisites

  • AWS Account with Ubuntu 22.04 LTS EC2 Instance
  • Minikube and kubectl Installed

Install Minikube and kubectl by following the official documentation for your operating system:

Minikube Installation Guide

Install Minikube on Ubuntu 22.04 LTS

  • Helm Installed:

Install Helm by following the official documentation:

Helm Installation Guide

helm lint command use case with Examples

The helm lint command is used to check a Helm chart for potential issues or errors. It performs a series of checks on the chart’s structure, syntax, and adherence to best practices. Here are some common use cases for using helm lint:

  1. Validate Chart Syntax:
    • helm lint checks the syntax of your Helm chart’s files, ensuring that they are correctly formatted and follow the expected syntax. This helps catch issues such as YAML syntax errors or misplaced template directives.
  2. Check Chart Structure:
    • The command examines the overall structure of your Helm chart to ensure that it adheres to the required conventions. It verifies the presence of necessary files and directories, such as the Chart.yaml file, templates directory, and others.
  3. Verify Template Rendering:
    • Helm uses templates to generate Kubernetes manifests based on the provided values. helm lint checks if the templates in your chart can be rendered successfully, catching potential errors before deploying the chart to a Kubernetes cluster.
  4. Ensure Chart Best Practices:
    • The command enforces Helm best practices and guidelines, helping you maintain a consistent and well-organized chart. It may provide warnings or errors for practices that could lead to issues during deployment.
  5. Prevent Common Mistakes:
    • helm lint helps prevent common mistakes and pitfalls by identifying issues in your Helm chart early in the development process. This includes detecting misconfigurations, missing required fields, or other problems that might cause deployment failures.

The helm lint command is used to validate your Helm charts and detect potential issues or errors before deploying them to a Kubernetes cluster.

This command takes a path to a chart and runs a series of tests to verify that the chart is well-formed.

If the linter encounters things that will cause the chart to fail installation, it will emit [ERROR] messages. If it encounters issues that break with convention or recommendation, it will emit [WARNING] messages.

To use helm lint, navigate to the directory containing your Helm chart and run the following command:

Syntax:

helm lint .
  • Replace . with the path to your Helm chart if it’s in a different directory.

If there are no issues, the command will display a message indicating that the linting was successful. If there are problems, helm lint will provide information about the errors or warnings encountered.

Example:

First we will create a chart named ‘world’ using helm create command. You can give any name you want to the chart.

helm create world
world 1

Then we will use helm lint command to validate the chart.

helm lint world

Following output shows the successful validation of the chart.

lint 1

This command will analyze your chart’s files, templates, and values for any potential issues or errors. If everything is correct, you will see a message indicating that the linting was successful.

Now lets see what will happen if there is an error.

We will modify the values.yaml file,

First navigate to world directory by using following command

cd world

Then we will modify and simply delete the line 8 from values.yaml file using following command

nano values.yaml

This command will open the values.yaml on your local machine. Edit it.

After modification press ctrl + x, then shift + y and Enter.

now exit the directory using following command.

cd

Now again run the helm lint command

helm lint world

If there are errors or warnings, you will receive feedback about what needs to be fixed as shown in below image.

failed lint 1

helm template command use case with Examples

The helm template command is used to render the Kubernetes YAML manifests from a Helm chart without installing it on a cluster. This is useful for inspecting the generated Kubernetes resources before deploying them to a cluster. Here are some common use cases for helm template:

1.Preview Manifests:

Use helm template to generate the Kubernetes YAML manifests based on the Helm chart. This allows you to inspect the resources that will be deployed to the cluster without actually applying them.

Syntax:

helm template <release-name> <chart-name> -f values.yaml

2. Debugging:

When encountering issues during Helm chart deployment, you can use helm template to examine the rendered manifests. This helps in debugging and identifying any problems with the generated Kubernetes resources or values.

3. Version Control and Auditing:

Syntax:

helm template <release-name> <chart-name> -f values.yaml > output.yaml

4. Customization and Overrides:

Preview the manifests with different configurations and overrides by specifying a different values file or using the --set option to dynamically change values.

Syntax:

helm template <release-name> <chart-name> --set key1=value1,key2=value2

5. Integration with CI/CD:

In a continuous integration/continuous deployment (CI/CD) pipeline, you might want to use helm template as a step to generate the Kubernetes manifests before applying them to the target Kubernetes cluster. This allows you to perform additional checks or validations before actual deployment.

6. Security and Compliance Checks:

Before deploying to a production environment, use helm template to inspect the generated manifests and perform security and compliance checks. This ensures that the deployed resources adhere to your organization’s policies and standards.

The helm template command takes a Helm chart as input and processes its templates, which are written in the Go template language. It replaces placeholders in the templates with actual values and generates the resulting Kubernetes manifest files.

Example:

First we will create a chart named ‘demo’ using helm create command. You can give any name you want to the chart.

helm create demo
image

then we will use helm template command to inspect rendered YAML.

helm template demo
image 1

Output:

# Source: demo/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: release-name-demo
  labels:
    helm.sh/chart: demo-0.1.0
    app.kubernetes.io/name: demo
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
automountServiceAccountToken: true
---
# Source: demo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: release-name-demo
  labels:
    helm.sh/chart: demo-0.1.0
    app.kubernetes.io/name: demo
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: demo
    app.kubernetes.io/instance: release-name
---
# Source: demo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: release-name-demo
  labels:
    helm.sh/chart: demo-0.1.0
    app.kubernetes.io/name: demo
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: demo
      app.kubernetes.io/instance: release-name
  template:
    metadata:
      labels:
        helm.sh/chart: demo-0.1.0
        app.kubernetes.io/name: demo
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      serviceAccountName: release-name-demo
      securityContext:
        {}
      containers:
        - name: demo
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}
---
# Source: demo/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "release-name-demo-test-connection"
  labels:
    helm.sh/chart: demo-0.1.0
    app.kubernetes.io/name: demo
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['release-name-demo:80']
  restartPolicy: Never

You can even change the values in values.yaml.

First navigate to the directory.

cd demo

then open the values.yaml file

nano values.yaml

Change the image reposiary shown below

image:
     repository: devopshint/node-app

After modification press ctrl + x, then shift + y and Enter.

Then lets modify deployment.yaml, navigate to template directory

cd template

open deployment.yaml using,

nano deployment.yaml

change the apiversion

apiversion: apps/v2

After modification press ctrl + x, then shift + y and Enter.

exit the directories using

cd

now run the template command again,

helm template demo

Output:

change

as you can see apiversion and image is successfully modified.

Now lets see what will happen if there is an error.

We will do the same steps like lint replacing ‘world’ with ‘demo’.

Now run the following command.

helm template demo

And if Yaml files have wrong indentation, spacing, incorrect values and YAML syntax. Then it will throw an error and also tells where it is.

image 2

helm –debug –dry-run command use case with Examples

The helm debug --dry-run command is used to simulate a Helm release without actually installing anything on the cluster. It’s a useful way to preview what changes Helm would make to your Kubernetes resources without affecting the actual cluster.

  1. Preview Helm Chart Changes:
    • When you run helm debug --dry-run, Helm goes through the process of rendering the templates in your Helm chart and applying values, but it doesn’t actually perform the deployment.
  2. Syntax and Template Checking:
    • The --dry-run flag allows you to catch potential errors in your Helm chart templates or values without making any changes to the live Kubernetes cluster. This is useful for verifying the syntax of your templates and ensuring they can be rendered correctly.
  3. Check Configuration and Resources:
    • It helps you check if the configuration values you’ve provided are being correctly applied to the Helm chart and whether the resulting Kubernetes resources are as expected.
  4. Avoid Unintended Changes:
    • Using --dry-run is a safety measure to prevent accidental changes to your Kubernetes cluster. By previewing the changes, you can make sure that you are deploying the right configuration before actually applying it.

Syntax:

helm install --dry-run --debug <release-name> <chart-name> -f values.yaml
  • --dry-run: Simulates the installation without making any changes to the cluster.
  • --debug: Prints the generated Kubernetes manifests to the console for inspection.
  • <release-name>: The name you want to give to the Helm release.
  • <chart-name>: The name or path to the Helm chart.
  • -f values.yaml: Specifies a values file to override default configuration.

Example:

First we will create a chart named ‘helloworld’ using helm create command. You can give any name you want to the chart.

helm create helloworld
helloworld

then we will run the helm install myrelease --debug --dry-run command. Where my release name is ‘hw’. You can give any release name.

helm install hw --debug --dry-run
dryrun

This command will output the generated manifests to the console without actually installing the chart along with NOTES. You can use this output to verify that the generated manifests are correct.

Output:

install.go:214: [debug] Original chart version: ""
install.go:231: [debug] CHART PATH: /home/ubuntu/helloworld

NAME: hw
LAST DEPLOYED: Tue Dec  5 09:14:51 2023
NAMESPACE: default
STATUS: pending-install
REVISION: 1
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
autoscaling:
  enabled: false
  maxReplicas: 100
  minReplicas: 1
  targetCPUUtilizationPercentage: 80
fullnameOverride: ""
image:
  pullPolicy: IfNotPresent
  repository: nginx
  tag: ""
imagePullSecrets: []
ingress:
  annotations: {}
  className: ""
  enabled: false
  hosts:
  - host: chart-example.local
    paths:
    - path: /
      pathType: ImplementationSpecific
  tls: []
nameOverride: ""
nodeSelector: {}
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
replicaCount: 1
resources: {}
securityContext: {}
service:
  port: 80
  type: ClusterIP
serviceAccount:
  annotations: {}
  automount: true
  create: true
  name: ""
tolerations: []
volumeMounts: []
volumes: []

HOOKS:
---
# Source: helloworld/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "hw-helloworld-test-connection"
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['hw-helloworld:80']
  restartPolicy: Never
MANIFEST:
---
# Source: helloworld/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: hw-helloworld
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
automountServiceAccountToken: true
---
# Source: helloworld/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hw-helloworld
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
---
# Source: helloworld/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hw-helloworld
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: helloworld
      app.kubernetes.io/instance: hw
  template:
    metadata:
      labels:
        helm.sh/chart: helloworld-0.1.0
        app.kubernetes.io/name: helloworld
        app.kubernetes.io/instance: hw
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      serviceAccountName: hw-helloworld
      securityContext:
        {}
      containers:
        - name: helloworld
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}

NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=helloworld,app.kubernetes.io/instance=hw" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT

And if there’s an error then it will not simulate a release install.

To see that we will follow the same steps used as lint and template.

after exiting the directory run the following command

helm install hw --debug --dry-run helloworld

Output:

dryrunerror

similarly you can use helm upgrade --debug --dry-run which simulates the upgrade of an existing Helm release without applying the changes and allows you to preview the modifications before executing an actual upgrade.

First install the helm chart using following command

helm install hw helloworld

Output:

installed

Now lets modify the values.yaml file

Navigate to the directory using following command

cd helloworld

then, use following command to edit values.yaml, we will edit repicaCount=2, and write a message

message: Hello, World! This is a customized message.
nano values.yaml

press ctrl + x, then shift + y and Enter.

exit the directories using following command

cd

Now perform the helm upgrade –dry-run

helm upgrade hw --debug --dry-run helloworld

Output:

ubuntu@ip-172-31-35-53:~$ helm upgrade hw --debug --dry-run helloworld
upgrade.go:153: [debug] preparing upgrade for hw
upgrade.go:161: [debug] performing update for hw
upgrade.go:345: [debug] dry run for hw
Release "hw" has been upgraded. Happy Helming!
NAME: hw
LAST DEPLOYED: Wed Dec  6 11:24:56 2023
NAMESPACE: default
STATUS: pending-upgrade
REVISION: 2
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
autoscaling:
  enabled: false
  maxReplicas: 100
  minReplicas: 1
  targetCPUUtilizationPercentage: 80
fullnameOverride: ""
image:
  pullPolicy: IfNotPresent
  repository: nginx
  tag: ""
imagePullSecrets: []
ingress:
  annotations: {}
  className: ""
  enabled: false
  hosts:
  - host: chart-example.local
    paths:
    - path: /
      pathType: ImplementationSpecific
  tls: []
message: Hello, World! This is a customized message.
nameOverride: ""
nodeSelector: {}
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
replicaCount: 2
resources: {}
securityContext: {}
service:
  port: 80
  type: ClusterIP
serviceAccount:
  annotations: {}
  automount: true
  create: true
  name: ""
tolerations: []
volumeMounts: []
volumes: []

HOOKS:
---
# Source: helloworld/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "hw-helloworld-test-connection"
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['hw-helloworld:80']
  restartPolicy: Never
MANIFEST:
---
# Source: helloworld/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: hw-helloworld
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
automountServiceAccountToken: true
---
# Source: helloworld/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hw-helloworld
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
---
# Source: helloworld/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hw-helloworld
  labels:
    helm.sh/chart: helloworld-0.1.0
    app.kubernetes.io/name: helloworld
    app.kubernetes.io/instance: hw
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: helloworld
      app.kubernetes.io/instance: hw
  template:
    metadata:
      labels:
        helm.sh/chart: helloworld-0.1.0
        app.kubernetes.io/name: helloworld
        app.kubernetes.io/instance: hw
        app.kubernetes.io/version: "1.16.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      serviceAccountName: hw-helloworld
      securityContext:
        {}
      containers:
        - name: helloworld
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}

NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=helloworld,app.kubernetes.io/instance=hw" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT

This will simulate the changes without applying them. It’s a way to preview the changes that would be made without making them.

Comparison of helm lint vs helm –debug –dry-run vs helm template command

FeatureHelm LintHelm TemplateHelm install –dry-run
PurposeCheck Helm Chart ValidityGenerate Kubernetes YAMLSimulate a Release Install
Executionhelm lint <chart>helm template <chart>helm install –dry-run
OutputConsole OutputRendered Kubernetes YAMLDisplay Installation Info
Actual InstallationNo actual installationRendered Kubernetes YAMLNo actual installation
Error/Warning CheckChecks for Errors/WarningsNo error/warning checkingNo error/warning checking
ValidationValidates Chart StructureNo validation performedNo validation performed
CustomizationNo customization allowedValues can be overriddenValues can be overridden
Useful for DebuggingYes, identify chart issuesYes, inspect rendered YAMLYes, check install output
The table provides a concise comparison of key Helm commands — helm lint, helm template, helm install --dry-run. Each command serves a distinct purpose in Helm workflows, helping users generate Kubernetes manifests, check for issues in Helm charts, and simulate installations or upgrades without making actual changes to the cluster.

Conclusion

In summary, while helm template and helm install --dry-run focus on rendering YAML and simulating installations, helm lint specifically targets chart validation, ensuring that the Helm chart is well-structured and follows best practices. Each of these Helm commands serves different purposes, and their combined use can contribute to a smooth Helm chart development and deployment process. Together, these Helm commands can help you improve the quality of your Helm charts and troubleshoot complex Kubernetes issues.

Related Articles:

How to Create Helm Chart for NodeJS App on Minikube

Reference:

Helm official page

About Prasad Hole

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Share via
Copy link