{"id":228,"date":"2026-05-09T10:27:21","date_gmt":"2026-05-09T10:27:21","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=228"},"modified":"2026-05-09T10:27:21","modified_gmt":"2026-05-09T10:27:21","slug":"_helpers-tpl-file-in-helm-charts","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=228","title":{"rendered":"How to Use _helpers.tpl to Create Reusable Helm Templates"},"content":{"rendered":"<p>If you are working with <a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm charts<\/a>, you might have seen a <code>_helpers.tpl<\/code> file inside the <code>\/templates<\/code> folder. Most people ignore it without understanding what it is used for while deploying a chart.<\/p>\n<p>It is key feature to create reusable helm templates.<\/p>\n<p>In this guide, we will look at.<\/p>\n<ul>\n<li>What <code>_helpers.tpl<\/code> is and how it works.<\/li>\n<li>Hands-on example demonstrating its usage.<\/li>\n<\/ul>\n<p>Lets get started.<\/p>\n<h2 id=\"what-is-helperstpl\">What is _helpers.tpl?<\/h2>\n<p><strong><code>_helpers.tpl<\/code><\/strong> is a reusable block that can be used in the Helm template files inside the <strong><code>\/templates<\/code><\/strong> folder.<\/p>\n<p>For example, <a href=\"https:\/\/devopscube.com\/kubernetes-objects-resources\/\" rel=\"noreferrer\">Kubernetes resources<\/a> such as <a href=\"https:\/\/devopscube.com\/kubernetes-deployment-tutorial\/\" rel=\"noreferrer\">Deployments<\/a>, Services, and other objects often use the same labels. <\/p>\n<p>For example,<\/p>\n<pre><code class=\"language-bash\">app.kubernetes.io\/name: web-app\napp.kubernetes.io\/managed-by: Helm\napp.kubernetes.io\/instance: web-app-{release}\napp.kubernetes.io\/component: frontend\napp.kubernetes.io\/version: 1.0.0<\/code><\/pre>\n<p>Usually we add the same labels in every resource template file.<\/p>\n<p>Now, lets say you want to add or remove a label. For this you have to update the it in every template file.<\/p>\n<p>What if you define the label in one place and it gets updated in all the templates?<\/p>\n<p>It is what <strong><code>_helpers.tpl<\/code><\/strong> solves. You just need to declare the block in <strong><code>_helpers.tpl<\/code><\/strong> file using the define field as shown below.<\/p>\n<pre><code>{{- define \"web-app.labels\" -}}\napp.kubernetes.io\/name: {{ .Chart.Name }}\napp.kubernetes.io\/managed-by: {{ .Release.Service }}\n{{- end }}<\/code><\/pre>\n<p>In Helm terms, <strong>you call that block as a named template or a partial. <\/strong> It simply means reusable template snippets.<\/p>\n<p>Next, the reusable block can be called by other resource templates like <code>deployment.yaml<\/code>, <code>service.yaml<\/code> etc using the <strong><code>include<\/code> <\/strong> field as shown below.<\/p>\n<pre><code class=\"language-yaml\">metadata:\n  labels:\n    {{- include \"web-app.labels\" . | nindent 4 }}<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">The indent 4 field is used to place the labels after 4 indentation.<\/div>\n<\/div>\n<p>This way, even <strong>if you need to change something<\/strong>, just update it in the <strong><code>_helpers.tpl<\/code><\/strong> file and it gets applied to all the templates that references it.  <\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/05\/image-8.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"1460\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/05\/image-8.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/05\/image-8.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/05\/image-8.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/05\/image-8.png 2388w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Another interesting features is <strong>you can substitute values<\/strong> from <code>chart.yaml<\/code> and <code>values.yaml<\/code> files in the <code>_helpers.tpl<\/code>. For example, you might want to add the image name, version and app details dynamically to the object metadata.<\/p>\n<p>Here is an example.<\/p>\n<pre><code class=\"language-bash\">{{- define \"web-app.labels\" }}\napp.kubernetes.io\/name: {{ .Chart.Name }}\napp.kubernetes.io\/version: {{ .Chart.Version }}\napp.kubernetes.io\/managed-by: {{ .Release.Service }}\nenvironment: {{ .Values.env }}\nimage: \"{{ .Values.image.repository }}:{{ .Values.image.tag }}\"\n{{- end }}<\/code><\/pre>\n<p>The following image illustrates the high-level flow of how <strong><code>_helpers.tpl<\/code><\/strong> works in a Helm chart.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/05\/image-6.png\" class=\"kg-image\" alt=\"image illustrating the high-level flow of how _helpers.tpl works in a Helm chart.\" loading=\"lazy\" width=\"2000\" height=\"1598\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/05\/image-6.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/05\/image-6.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/05\/image-6.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2026\/05\/image-6.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>It is somewhat similar to <a href=\"https:\/\/devopscube.com\/jenkins-shared-library-tutorial\/\" rel=\"noreferrer\">Jenkins shared library<\/a> or GitHub Actions reusable workflows, but scoped to a helm chart.<\/p>\n<p>In short, <strong><code>_helpers.tpl<\/code><\/strong> is a recommended file to <strong>store template partials.<\/strong><\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Key Insight:<\/strong><\/b> Helm treats files starting with <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">_<\/code> as helper\/partial files and does not render them as <a href=\"https:\/\/devopscube.com\/kubernetes-tutorials-beginners\/\" rel=\"noreferrer\">Kubernetes<\/a> manifests. However, you can use the defined block in any chart template. Same logic applies to <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">_helpers.tpl<\/code><\/div>\n<\/div>\n<h2 id=\"helperstpl-hands-on-example\">helpers.tpl Hands On Example<\/h2>\n<p>Now lets look at a hands on example on how we can use <code>_helpers.tpl<\/code> for partials in a Helm chart.<\/p>\n<p>Here is what we are going to do.<\/p>\n<ul>\n<li>We will create a <strong><code>_helpers.tpl<\/code><\/strong> which defines the labels used in the deployment and services templates.<\/li>\n<li>Also, it gets label values from <code>chart.yaml<\/code> and <code>values.yaml<\/code> files.<\/li>\n<li>During installation, the label block will be included in both the deployment and services templates.<\/li>\n<\/ul>\n<p>Lets get started.<\/p>\n<h3 id=\"step-1-create-a-helm-boilerplate\">Step 1: Create a Helm Boilerplate<\/h3>\n<p>To start, lets create a Helm boilerplate with the following command.<\/p>\n<pre><code class=\"language-bash\">helm create web-app<\/code><\/pre>\n<p>This will create a Helm chart directory structure, from that remove the values file and files inside the \/templates folder, which we will create with the TPL file.<\/p>\n<pre><code class=\"language-bash\">rm -rf web-app\/templates\/*\nrm web-app\/values.yaml<\/code><\/pre>\n<h3 id=\"step-2-create-a-valuesyaml-file\">Step 2: Create a values.yaml file<\/h3>\n<p>Run the following command to create the values file.<\/p>\n<pre><code class=\"language-bash\">cat &gt; web-app\/values.yaml &lt;&lt; 'EOF'\nreplicaCount: 1\n\nimage:\n  repository: nginx\n  tag: \"1.25\"\n\nservice:\n  type: ClusterIP\n  port: 80\n\nenv: stage\nEOF<\/code><\/pre>\n<p>In here, we added an env field, which we will be using in a label block with the .tpl file.<\/p>\n<h3 id=\"step-3-write-the-helperstpl-file\">Step 3: Write the _helpers.tpl File<\/h3>\n<p>Lets create a simple <strong><code>.tpl<\/code><\/strong> file using the following command.<\/p>\n<pre><code class=\"language-bash\">cat &gt; web-app\/templates\/_helpers.tpl &lt;&lt; 'EOF'\n{{- define \"web-app.labels\" -}}\napp: {{ .Release.Name }}\nversion: {{ .Chart.AppVersion }}\nmanaged-by: Helm\nenv: {{ .Values.env }}\n{{- end }}\nEOF<\/code><\/pre>\n<p>In this partial, we defined four labels.<\/p>\n<ul>\n<li><strong>app name<\/strong> &#8211; This will be based on the Helm release name<\/li>\n<li><strong>version<\/strong> &#8211; Uses the app version in the Charts.yaml file<\/li>\n<li><strong>managed-by<\/strong> &#8211; This is static and specified as Helm<\/li>\n<li><strong>env<\/strong> &#8211; This label will be used from the values file<\/li>\n<\/ul>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udccc<\/div>\n<div class=\"kg-callout-text\">In this TPL file, we only added the label block. What you want to add depends upon your specific use case.<\/div>\n<\/div>\n<h3 id=\"step-4-create-a-deployment-and-service-template-with-partials\">Step 4: Create a Deployment and Service Template With Partials<\/h3>\n<p>In this step we will create a deployment and service template that includes the partial from the helper file.<\/p>\n<p>Lets create a deployment Helm template with the label partial.<\/p>\n<pre><code class=\"language-bash\">cat &gt; web-app\/templates\/deployment.yaml &lt;&lt; 'EOF'\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: {{ .Release.Name }}\n  labels:\n    {{- include \"web-app.labels\" . | nindent 4 }}\nspec:\n  replicas: {{ .Values.replicaCount }}\n  selector:\n    matchLabels:\n      app: {{ .Release.Name }}\n  template:\n    metadata:\n      labels:\n        {{- include \"web-app.labels\" . | nindent 8 }}\n    spec:\n      containers:\n        - name: nginx\n          image: \"{{ .Values.image.repository }}:{{ .Values.image.tag }}\"\n          ports:\n            - containerPort: 80\nEOF<\/code><\/pre>\n<p>Now run the following to create a service template file with the label partial.<\/p>\n<pre><code>cat &gt; web-app\/templates\/service.yaml &lt;&lt; 'EOF'\napiVersion: v1\nkind: Service\nmetadata:\n  name: {{ .Release.Name }}\n  labels:\n    {{- include \"web-app.labels\" . | nindent 4 }}\nspec:\n  type: {{ .Values.service.type }}\n  selector:\n    app: {{ .Release.Name }}\n  ports:\n    - protocol: TCP\n      port: {{ .Values.service.port }}\n      targetPort: 80\nEOF<\/code><\/pre>\n<p>In both files we created, you can see that we <strong>called the <code>.tpl<\/code> file using <code>include \"web-app.labels\"<\/code>.<\/strong> The partial we defined inside the <code>.tpl<\/code> file will be substituted here.<\/p>\n<h3 id=\"step-5-validate-the-helm-chart\">Step 5: Validate the Helm Chart<\/h3>\n<p>Before installing the chart, lets render it and see if all labels are adding correctly.<\/p>\n<pre><code class=\"language-bash\">helm template web-app .\/web-app<\/code><\/pre>\n<p>You will get an output of the YAML files with the labels rendered from the partial in <strong><code>_helper.tpl<\/code><\/strong><\/p>\n<p>For example, the label block will be like this.<\/p>\n<pre><code class=\"language-bash\">  labels:\n    app: web-app\n    version: 1.16.0\n    managed-by: Helm\n    env: stage<\/code><\/pre>\n<h3 id=\"step-6-install-the-chart\">Step 6: Install the Chart<\/h3>\n<p>Now that the rendering checks if it runs without any issue, let&#8217;s install the chart.<\/p>\n<pre><code class=\"language-bash\">helm install web-app .\/web-app<\/code><\/pre>\n<p>You will get an output as follows.<\/p>\n<pre><code class=\"language-bash\">NAME: web-app\nLAST DEPLOYED: Tue Apr 28 14:41:52 2026\nNAMESPACE: default\nSTATUS: deployed\nREVISION: 1\nDESCRIPTION: Install complete<\/code><\/pre>\n<p>And, check the labels of the deployment, you can see all the labels added to it that we defined in the partial.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get deploy web-app --show-labels\n\nNAME      READY   UP-TO-DATE   AVAILABLE   AGE   LABELS\n\nweb-app   1\/1     1            1           46s   app.kubernetes.io\/managed-by=Helm,app=web-app,env=stage,managed-by=Helm,version=1.16.0<\/code><\/pre>\n<h3 id=\"step-7-clean-up\">Step 7: Clean Up<\/h3>\n<p>To uninstall Helm use the following command.<\/p>\n<pre><code class=\"language-bash\">helm uninstall web-app<\/code><\/pre>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>In summary, <strong><code>_helpers.tpl<\/code><\/strong> is a simple file that helps you avoid duplicating blocks in Helm templates.<\/p>\n<p>Instead of copying the same block in multiple files, just define the block in the <code>_helpers.tpl<\/code> file and call it using the include field inside the templates.<\/p>\n<p>Here are the key things to remember.<\/p>\n<ul>\n<li>It should be used for <strong>reusable template logic<\/strong>, not for putting large amounts of application configuration or complicated business logic. <\/li>\n<li>Dont turn Helm into a programming language with deeply nested or complex logics in partials.<\/li>\n<\/ul>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/_helpers-tpl-file-in-helm-charts\/\" target=\"_blank\" rel=\"noopener noreferrer\">How to Use _helpers.tpl to Create Reusable Helm Templates \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/_helpers-tpl-file-in-helm-charts\/<\/p>\n","protected":false},"author":1,"featured_media":229,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-228","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/228","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=228"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/228\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/229"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}