{"id":944,"date":"2023-06-24T11:16:22","date_gmt":"2023-06-24T11:16:22","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=944"},"modified":"2023-06-24T11:16:22","modified_gmt":"2023-06-24T11:16:22","slug":"kustomize-tutorial","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=944","title":{"rendered":"Kubernetes Kustomize Tutorial (Comprehensive Guide)"},"content":{"rendered":"<p>In this Kustomize Tutorial, you will learn all the Kustomize concepts and deploy an application using Kustomize on a Kubernetes cluster.<\/p>\n<h2 id=\"kustomize-use-case\">Kustomize Use Case<\/h2>\n<p>Before diving into Kustomize, let\u2019s understand the problem with deploying applications using <a href=\"https:\/\/devopscube.com\/create-kubernetes-yaml\/\">Kubernetes manifests.<\/a><\/p>\n<p>Suppose you want to deploy applications to <a href=\"https:\/\/devopscube.com\/kubernetes-tutorials-beginners\/\">Kubernetes<\/a> and you have multiple environments i.e. <strong>dev, uat, prod<\/strong> etc. In each environment, you might have <strong>different configurations<\/strong> for the deployments.<\/p>\n<p>For example, in dev and uat you might not need rolling updates however in prod you might need it. Also, you might want different replicas in each environment, different CPU &amp; memory resources,  annotations, etc. Not only that, applications could use properties through Configmaps and Secrets that change for every environment.<\/p>\n<p>Hence, you need to customize the deployments to accommodate the requirements for the respective environment.<\/p>\n<p>The simple solution to this problem is to create three separate directories, one for each environment, and add all the Kubernetes manifest in respective folders.<\/p>\n<p>But it is not a scalable solution. Because when new applications get onboarded or new configuration files get added, it would be hard to manually manage all the YAML files in the folders. This could also lead to <strong>configuration drift<\/strong> issues.<\/p>\n<p>You could create scripts to replace configs in the YAML but it would not be a good approach when you have many services.<\/p>\n<p>All these issues could be resolved using <strong>Kustomize<\/strong>. Also, one feature that sets it apart from other configuration tools is its tight integration with kubectl, the command-line interface for managing Kubernetes clusters.<\/p>\n<p>In the following topics, we will look at the Kustomize concepts and its benefits in detail. We will also look at a practical example of Kustomize using an Nginx Deployment to show you how it simplifies Kubernetes deployments.<\/p>\n<h2 id=\"what-is-kustomize\">What is Kustomize?<\/h2>\n<p><a href=\"https:\/\/github.com\/kubernetes-sigs\/kustomize\/tree\/master?ref=devopscube.com\" rel=\"noreferrer noopener\">Kustomize<\/a> is an open-source <a href=\"https:\/\/devopscube.com\/infrastructure-as-code-configuration-management\/\" rel=\"noreferrer noopener\">configuration management<\/a> tool for Kubernetes.<\/p>\n<p>It allows you to define and manage <a href=\"https:\/\/devopscube.com\/kubernetes-objects-resources\/\">Kubernetes objects<\/a> such as <a href=\"https:\/\/devopscube.com\/kubernetes-deployment-tutorial\/\">deployments<\/a>, <a href=\"https:\/\/devopscube.com\/kubernetes-daemonset\/\">Daemonsets<\/a>, services, configMaps, etc for multiple environments in a declarative manner without modifying the original YAML files. To put it simply, you have a single source of truth for YAMLs, and you patch required configurations on top of the base YAMLs as per the environment requirements.<\/p>\n<p>Here is what the official documentation says<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\">kustomize lets you customize raw, <b><strong style=\"white-space: pre-wrap;\">template-free YAML files<\/strong><\/b> for multiple purposes, leaving the original YAML untouched and usable as is.<\/div>\n<\/div>\n<p>Kustomize has two key concepts, <strong>Base and Overlays<\/strong>. With Kustomize we can reuse the base files (common YAMLs) across all environments and<strong> overlay <\/strong>(patches) specifications for each of those environments.<\/p>\n<p>Overlaying is the process of creating a customized version of the manifest file (<strong>base manifest + overlay manifest = customized manifest<\/strong> file).<\/p>\n<p>All customization specifications are contained within a <code><strong>kustomization.yaml<\/strong> <\/code>file.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-29-22.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1881\" height=\"2016\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-29-22.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-29-22.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/image-29-22.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-29-22.png 1881w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<h2 id=\"kustomize-features\">Kustomize Features<\/h2>\n<p>The following are the key features of Kustomize<\/p>\n<ol>\n<li>Acts as a configuration tool with declarative configuration same as Kubernetes YAMLs.<\/li>\n<li>It can modify resources without altering the original files.<\/li>\n<li>It can add common labels and annotations to all the resources.<\/li>\n<li>It can Modify container images based on the environment it is being deployed in.<\/li>\n<li>Kustomize also ships with <code>secretGenerator<\/code> and <code>configMapGenerator<\/code> that use environment files or key-value pairs to create secrets and configMaps.<\/li>\n<\/ol>\n<p>All these concepts and features will make more sense in the section where I <strong>practically show you<\/strong> how to use Kustomize using an nginx deployment.<\/p>\n<h2 id=\"install-kustomize\">Install Kustomize<\/h2>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note:<\/strong><\/b> Before installing Kustomize, you must have a Kubernetes cluster up and running and kubectl installed in our local machine and connected to the cluster.<\/div>\n<\/div>\n<p>Kustomize is an open-source tool and is available as a standalone binary or as a plugin for kubectl. The installation of Kustomize is very easy.<\/p>\n<h3 id=\"kubectl-kustomize\">Kubectl Kustomize<\/h3>\n<p>The kustomize module is built into kubectl. You can use customize directly via kubectl. You can verify it using the following command.<\/p>\n<pre><code>kubectl kustomize --help<\/code><\/pre>\n<h3 id=\"standalone-kustomize\">Standalone Kustomize<\/h3>\n<p>The below-mentioned script automatically detects the OS and installs the Kustomize.<\/p>\n<pre><code>curl -s \"https:\/\/raw.githubusercontent.com\/kubernetes-sigs\/kustomize\/master\/hack\/install_kustomize.sh\"  | bash<\/code><\/pre>\n<p>After installation, verify it by running the below command. It will show you the latest appropriate version of Kustomize. If it&#8217;s not showing the version then close your terminal and run the command by opening a fresh terminal.<\/p>\n<pre><code>kustomize version<\/code><\/pre>\n<p>If you are still getting <code>bash: kustomize: command not found error<\/code> then run the below command and check again. This will set the path.<\/p>\n<pre><code>sudo install -o root -g root -m 0755 kustomize \/usr\/local\/bin\/kustomize<\/code><\/pre>\n<p>We can also install Kustomize using <code>brew<\/code> and <code>chocolatey<\/code> on MAC and Windows respectively.<\/p>\n<p>For MAC users:<\/p>\n<pre><code>brew install kustomize<\/code><\/pre>\n<p>For Windows users:<\/p>\n<pre><code>choco install kustomize<\/code><\/pre>\n<h2 id=\"understanding-kustomize\">Understanding Kustomize<\/h2>\n<p>First, you need to understand the following Key Kustomize concepts.<\/p>\n<ol>\n<li><code>kustomization.yaml<\/code>file<\/li>\n<li>Base and Overlays<\/li>\n<li>Transformers<\/li>\n<li>Patches<\/li>\n<\/ol>\n<p>Let&#8217;s take a look at each concept.<\/p>\n<h3 id=\"kustomizationyaml-file\"><code>kustomization.yaml <\/code>file<\/h3>\n<p>The <code>kustomization.yaml<\/code> file is the main file used by the Kustomize tool.<\/p>\n<p>When you execute Kustomize, it looks for the file named <code>kustomization.yaml<\/code>. This file contains a list of all of the Kubernetes resources (YAML files) that should be managed by Kustomize. It also contains all the customizations that we want to apply to generate the customized manifest.<\/p>\n<p>Here is an example <code>kustomization.yaml<\/code> file. Don&#8217;t worry about all the configurations. we will learn about all the fields in the following sections.<\/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\/2025\/03\/image-21-31.png\" class=\"kg-image\" alt=\"example kustomization.yaml file.\" loading=\"lazy\" width=\"557\" height=\"683\"><\/figure>\n<h3 id=\"base-and-overlays\">Base and Overlays<\/h3>\n<p>The Base folder represents the config that going to be identical across all the environments. We put all the Kubernetes manifests in the Base. It has a default value that we can overwrite.<\/p>\n<p>On the other side, the Overlays folder allows us to customize the behavior on a per-environment basis. We can create an Overlay for each one of the environments. We specify all the properties and parameters that we want to overwrite &amp; change.<\/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\/2025\/03\/kustomize2-drawio-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"671\" height=\"101\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/kustomize2-drawio-1.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/kustomize2-drawio-1.png 671w\"><\/figure>\n<p>Basically, Kustomize uses patch directive to introduce environment-specific changes on existing Base standard k8s config files without disturbing them. We will look at patches in a bit.<\/p>\n<h3 id=\"transformers\">Transformers<\/h3>\n<p>As the name indicates, transformers are something that transforms one config into another. Using Transformers, we can transform our base Kubernetes YAML configs. Kustomize has several built-in transformers. Let&#8217;s see some common transformers:<\/p>\n<ol>\n<li><strong><code>commonLabel<\/code><\/strong> &#8211; It adds a label to all Kubernetes resources<\/li>\n<li><strong><code>namePrefix<\/code><\/strong> &#8211; It adds a common prefix to all resource<br \/>names<\/li>\n<li><strong><code>nameSuffix<\/code><\/strong> &#8211; It adds a common suffix to all resource<br \/>names<\/li>\n<li><strong><code>Namespace<\/code><\/strong> &#8211; It adds a common namespace to all resources<\/li>\n<li><strong><code>commonAnnotations<\/code><\/strong> &#8211; It adds an annotation to all resources<\/li>\n<\/ol>\n<p>Let&#8217;s see an example. In the below image, we have used <code>commonLabels<\/code> in <code>kustomization.yaml <\/code>where label <code>env: dev<\/code> gets added to the customized <code>deployment.yaml.<\/code><\/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\/2025\/03\/image-9-29.png\" class=\"kg-image\" alt=\"Kustomize Transformers\" loading=\"lazy\" width=\"2000\" height=\"924\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-9-29.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-9-29.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/image-9-29.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-9-29.png 2325w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<h4 id=\"image-transformer\">Image Transformer<\/h4>\n<p>It allows us to modify an image that a specific deployment is going to use.<\/p>\n<p>In the following example, the image transformer checks the <code>nginx<\/code> image name as mentioned  <code>deployment.yaml<\/code> and changes it to the new name which is <code>ubuntu<\/code> in the <code>kustomization.yaml<\/code> file. We can change the tags as well.<\/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\/2025\/03\/image-10-37.png\" class=\"kg-image\" alt=\"Kustomize Image Transformer\" loading=\"lazy\" width=\"2000\" height=\"967\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-10-37.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-10-37.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/image-10-37.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-10-37.png 2088w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<h3 id=\"patches-overlays\">Patches (Overlays)<\/h3>\n<p>Patches or overlays provide another method to modify Kubernetes configs. It provides more specific sections to change in the configuration. There are 3 parameters we need to provide:<\/p>\n<ol>\n<li><code>Operation Type:<\/code> add or remove or replace<\/li>\n<li><code>Target:<\/code> Resource name which we want to modify<\/li>\n<li><code>Value:<\/code> Value name that will either be added or replaced. For the remove operation type, there would not be any value.<\/li>\n<\/ol>\n<p>There are two ways to define the patch:<\/p>\n<ol>\n<li>JSON 6902 and<\/li>\n<li>Stragetic Merge Patching.<\/li>\n<\/ol>\n<h4 id=\"json-6902-patching\">JSON 6902 Patching<\/h4>\n<p>In this way, there are two details that we have to provide, the <strong>target<\/strong> and the <strong>patch details<\/strong> i.e. operation, path, and the new value.<\/p>\n<pre><code>patches:\n  - target:\n      kind: Deployment\n      name: web-deployment\n    patch: |-\n      - op: replace\n        path: \/spec\/replicas\n        value: 5<\/code><\/pre>\n<p>The following image shows the JSON patching workflow.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-11-32.png\" class=\"kg-image\" alt=\"Kustomize JSON patching\" loading=\"lazy\" width=\"2000\" height=\"819\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-11-32.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-11-32.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/image-11-32.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-11-32.png 2360w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<h4 id=\"stragetic-merge-patching\">Stragetic Merge Patching<\/h4>\n<p>In this way, all the patch details are similar to a standard k8s config. It would be the original manifest file, we just add the fields that need to be modified.<\/p>\n<p>Here is an example of inline Stragetic Merge Patching.<\/p>\n<pre><code>patches:\n  - patch: |-\n      apiVersion: apps\/v1\n      kind: Deployment\n      metadata:\n        name: web-deployment\n      spec:\n        replicas: 5<\/code><\/pre>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-12-27.png\" class=\"kg-image\" alt=\"Kustomize Stragetic Merge Patching\" loading=\"lazy\" width=\"2000\" height=\"745\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-12-27.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-12-27.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/image-12-27.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-12-27.png 2088w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<h4 id=\"patch-from-file\">Patch From File<\/h4>\n<p>For both types of patching, instead of inline configs, we can use the separate file method. Specify all the patch details in a YAML file and refer it to the <code>kustomization.yaml<\/code> file under the patches directive.<\/p>\n<p>For example, in <code>kustomization.yaml<\/code> you need to mention the patch file as follows. You need to specify the relative path of the YAML file.<\/p>\n<pre><code>patches:\n- path: replicas.yaml<\/code><\/pre>\n<p>And we can put the changes in <code>replicas.yaml<\/code> as given below.<\/p>\n<pre><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: web-deployment\nspec:\n  replicas: 5<\/code><\/pre>\n<p>Now that we have a good understanding of all the underlying Kustomize concepts, let&#8217;s put our learnings into a hands-on implementation.<\/p>\n<h2 id=\"deploy-application-using-kustomize\">Deploy Application Using Kustomize<\/h2>\n<p>Let\u2019s see how Kustomize works using a real-world deployment scenario involving different environments.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: For demonstration purposes, we have given a simple YAML files with only two environments. In actual project, the YAML could be more complex with different objects and more deployment environemnts.<\/div>\n<\/div>\n<p>Let&#8217;s assume the following scenario.<\/p>\n<ol>\n<li>Nginx web server needs to be deployed in the dev and prod<\/li>\n<li>In dev, we need only a deployment with 2 replicas, a Nodeport service, and less memory and CPU resources.<\/li>\n<li>In prod, we need a deployment with 4 replicas, different CPU and memory limits, a rolling update strategy, and a service without NodePort.<\/li>\n<\/ol>\n<p>Let&#8217;s see how we can achieve this using Kustomize.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Github Repo:<\/strong><\/b> All the manifest used in this guide are hosted in the <a href=\"https:\/\/github.com\/techiescamp\/kustomize?ref=devopscube.com\" rel=\"noreferrer noopener\">Kustomize Github Repo<\/a>.<\/div>\n<\/div>\n<p>Here is the directory structure for using Kustomize.<\/p>\n<pre><code>\u251c\u2500\u2500 kustomize\n  \u251c\u2500\u2500 base\n    \u2502   \u251c\u2500\u2500 deployment.yaml\n    \u2502   \u251c\u2500\u2500 service.yaml\n    \u2502   \u251c\u2500\u2500 kustomization.yaml\n    \u2514 overlays\n        \u251c\u2500\u2500 dev\n        \u2502   \u251c\u2500\u2500 deployment-dev.yaml\n        |   \u251c\u2500\u2500 service-dev.yaml\n        \u2502   \u2514\u2500\u2500 kustomization.yaml\n        \u2514\u2500\u2500 prod\n            \u251c\u2500\u2500 deployment-prod.yaml\n            \u251c\u2500\u2500 service-prod.yaml\n            \u2514\u2500\u2500 kustomization.yaml<\/code><\/pre>\n<p>You can either use the GitHub repo files as a reference or create the respective folders and files using the following commands:<\/p>\n<pre><code>mkdir -p kustomize\/base &amp;&amp; \n    touch kustomize\/base\/deployment.yaml \\\n         kustomize\/base\/service.yaml \\\n         kustomize\/base\/kustomization.yaml &amp;&amp; \n    mkdir -p kustomize\/overlays\/dev &amp;&amp; \n    touch kustomize\/overlays\/dev\/deployment-dev.yaml \\\n         kustomize\/overlays\/dev\/service-dev.yaml \\\n         kustomize\/overlays\/dev\/kustomization.yaml &amp;&amp; \n    mkdir -p kustomize\/overlays\/prod &amp;&amp; \n    touch kustomize\/overlays\/prod\/deployment-prod.yaml \\\n         kustomize\/overlays\/prod\/service-prod.yaml \\\n         kustomize\/overlays\/prod\/kustomization.yaml<\/code><\/pre>\n<p>Let&#8217;s start with the base folder.<\/p>\n<h3 id=\"base-folder\">Base Folder<\/h3>\n<p>The base folder contains the deployment, service, and kustomization files. In this base folder, we add the deployment and service YAML with all the configs which could be common for all the environments.<\/p>\n<p><strong><code>base\/deployment.yaml<\/code><\/strong><\/p>\n<pre><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: web-deployment\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: web\n  template:\n    metadata:\n      labels:\n        app: web\n    spec:\n      containers:\n      - name: nginx\n        image: nginx:1.14.2\n        ports:\n        - containerPort: 80<\/code><\/pre>\n<p><strong><code>base\/service.yaml<\/code><\/strong><\/p>\n<pre><code>apiVersion: v1\n  kind: Service\n  metadata:\n    name: web-service\n  spec:\n    selector:\n      app: web\n    ports:\n    - name: http\n      port: 80<\/code><\/pre>\n<p><strong><code>base\/kustomization.yaml<\/code><\/strong><\/p>\n<p>In the below file, we&#8217;re referring <code>deployment.yaml<\/code> and <code>service.yaml <\/code>as resources.<\/p>\n<pre><code>apiVersion: kustomize.config.k8s.io\/v1beta1\nkind: Kustomization\n  \nresources:\n- deployment.yaml\n- service.yaml<\/code><\/pre>\n<h3 id=\"dev-overlay-folder\">Dev Overlay Folder<\/h3>\n<p>Let&#8217;s define Dev overlays files. We only want to change in <code>deployment.yaml<\/code> so we will only define it.<\/p>\n<p><strong><code>deployment-dev.yaml<\/code><\/strong><\/p>\n<p>In dev deployment, we only want to increase the replicas from 1 to 2. You can see we only define the changes, not other things. Kustomize will check the base deployment file and compare it and patch the changes accordingly. That&#8217;s the beauty of Kustomize.<\/p>\n<pre><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: web-deployment\nspec:\n  replicas: 3 # Update the replica count to 3\n  template:\n    spec:\n      containers:\n      - name: nginx\n        resources:\n          limits:\n            cpu: \"200\" # Lower CPU limit to 200m (0.2 CPU cores)\n            memory: \"256Mi\" # Lower memory limit to 256 MiB\n          requests:\n            cpu: \"100\" # Lower CPU request to 100m (0.1 CPU cores)\n            memory: \"128Mi\"<\/code><\/pre>\n<p><strong><code>service-dev.yaml<\/code><\/strong><\/p>\n<p>In dev, we need the service with a nodeport. So we will create an overlay with the type Nodeport.<\/p>\n<pre><code>apiVersion: v1\nkind: Service\nmetadata:\n  name: web-service\nspec:\n  type: NodePort<\/code><\/pre>\n<p><strong><code>kustomization.yaml<\/code><\/strong><\/p>\n<p>We&#8217;re using Strategic Merge Patching using the separate file method as we discussed earlier in the blog. You can also notice we have defined resources here also, this is why Kustomize needs to know the path of base files.<\/p>\n<p>Note that in older Kustomize versions, bases can be used in place of resources here.<\/p>\n<pre><code>apiVersion: kustomize.config.k8s.io\/v1beta1\nkind: Kustomization\n\nresources:\n- ..\/..\/base\n\npatches:\n- path: deployment-dev.yaml\n- path: service-dev.yaml<\/code><\/pre>\n<h4 id=\"review-apply-patches\">Review &amp; Apply Patches<\/h4>\n<p>Let&#8217;s review the patches. We can use the below command to review the patches and check whether everything is correct or not.<\/p>\n<pre><code>kustomize build overlays\/dev<\/code><\/pre>\n<p>It will render the below Kubernetes manifest as shown below.<\/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\/2025\/03\/image-24-27.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"603\" height=\"839\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-24-27.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-24-27.png 603w\"><\/figure>\n<p>As you can see the number of <strong>replicas in deployment increased to 2<\/strong>, and different CPU and memory resources and service type changed to NodePort. Now this is the desired configuration for the dev environment.<\/p>\n<p>We can deploy the customized manifest using the following command.<\/p>\n<pre><code>kustomize build overlays\/dev | kubectl apply -f -<\/code><\/pre>\n<p>You can also use the following kubectl command.<\/p>\n<pre><code>kubectl apply -k overlays\/dev<\/code><\/pre>\n<h3 id=\"prod-overlay-folder\">Prod Overlay Folder<\/h3>\n<p><strong><code>deployment-prod.yaml<\/code><\/strong><\/p>\n<p>In prod deployment, we&#8217;re adding the RollingUpdate strategy with 4 replicas of the deployment and different memory and CPU resources.<\/p>\n<pre><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: web-deployment\nspec:\n  template:\n    replicas: 4 # Update the replica count to 3\n    spec:\n      containers:\n      - name: nginx\n        resources:\n          limits:\n            cpu: \"1\" # Lower CPU limit to 200m (0.2 CPU cores)\n            memory: \"1Gi\" # Lower memory limit to 256 MiB\n          requests:\n            cpu: \"500\" # Lower CPU request to 100m (0.1 CPU cores)\n            memory: \"512Mi\" # Lower memory request to 128 MiB\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 1\n      maxUnavailable: 1<\/code><\/pre>\n<p><strong><code>service-prod.yaml<\/code><\/strong><\/p>\n<p>We&#8217;re changing the service type to NodePort.<\/p>\n<pre><code>apiVersion: v1\nkind: Service\nmetadata:\n  name: web-service\nspec:\n  type: NodePort<\/code><\/pre>\n<p><strong><code>kustomization.yaml<\/code><\/strong><\/p>\n<p>In the <strong><code>kustomization.yaml<\/code><\/strong> I have added the absolute path of both files for patching as we want to make some changes in prod.<\/p>\n<pre><code>apiVersion: kustomize.config.k8s.io\/v1beta1\nkind: Kustomization\n\nresources:\n- ..\/..\/base\n\npatches:\n- path: deployment-prod.yaml\n- path: service-prod.yaml<\/code><\/pre>\n<p>Run the below command again for reviewing the configuration and patches.<\/p>\n<pre><code>kustomize build overlays\/prod<\/code><\/pre>\n<p>Alternatively, you can use Kustomize with the kubectl command as follows.<\/p>\n<pre><code>kubectl kustomize overlays\/prod<\/code><\/pre>\n<h4 id=\"review-apply-patches-1\">Review &amp; Apply Patches<\/h4>\n<p>If everything looks good we can deploy it now by running the below command.<\/p>\n<pre><code>kustomize build overlays\/prod | kubectl apply -f -<\/code><\/pre>\n<p>Alternatively, you can use the kubectl command as follows.<\/p>\n<pre><code>kubectl apply -k overlays\/prod<\/code><\/pre>\n<p>After deploying it, we can check the objects by running the below commands.<\/p>\n<pre><code>kubectl get deployments\nkubectl get services\nkubectl get pods<\/code><\/pre>\n<p>Use the following command to view all the objects at once.<\/p>\n<pre><code>kubectl get all<\/code><\/pre>\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\/2025\/03\/image-20-30.png\" class=\"kg-image\" alt=\"Object created by Kustomize\" loading=\"lazy\" width=\"1062\" height=\"512\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-20-30.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-20-30.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-20-30.png 1062w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<h2 id=\"kustomize-configmap-and-secret-generators\">Kustomize Configmap and Secret Generators<\/h2>\n<p>Kustomize has the functionality to generate Configmaps and Secrets.<\/p>\n<p>In Kustomization YAML there are two supported fields<\/p>\n<ol>\n<li>configMapGenerator and<\/li>\n<li>secretGenerator<\/li>\n<\/ol>\n<p>Check out the <a href=\"https:\/\/devopscube.com\/kuztomize-configmap-generators\/\">Kuztomize Configmap Generators<\/a> guide where the use case and workflow are explained practically.<\/p>\n<h2 id=\"kustomize-troubleshootingcommon-issues\">Kustomize Troubleshooting &#8211; Common Issues<\/h2>\n<ol>\n<li>Kustomize version should be compatible with the Kubernetes cluster&#8217;s version as Kustomize might introduce features or changes that aren&#8217;t supported by older Kubernetes versions.<\/li>\n<li>If you&#8217;re using overlays to apply customizations, check if you&#8217;ve properly specified the overlays in your <code>kustomization.yaml<\/code> file. Each overlay must be listed under the <code>resources<\/code> field with the correct path. Make sure you&#8217;re running <code>kustomize build<\/code> or <code>kustomize build &lt;overlay-path&gt;<\/code> to generate the final configuration with the applied changes.<\/li>\n<li>If you&#8217;re encountering an error message like <code>\"kustomize: command not found\"<\/code> make sure that Kustomize is installed and available in your system&#8217;s PATH.<\/li>\n<li>Kustomize uses YAML files for defining configurations and customizations. Ensure that your YAML files are properly formatted, indented correctly, and have valid syntax<\/li>\n<\/ol>\n<h2 id=\"benefits-of-using-kustomize\">Benefits of Using Kustomize<\/h2>\n<p>Below are the benefits of Kustomize<\/p>\n<ol>\n<li><strong>Simplified Configuration Management:<\/strong> Kustomize is easy to use and allows you to manage and customize your Kubernetes configurations more easily by enabling you to define your configuration in a structured and modular way.<\/li>\n<li><strong>Reusability:<\/strong> With Kustomize we can reuse one of the base files across all environments and overlay specifications for each of those environments. This can save you time and effort by allowing you to reuse common configurations rather than having to create them from scratch for each new deployment.<\/li>\n<li><strong>Version Control:<\/strong> Kustomize allows you to version control your Kubernetes configurations, making it easier to track changes and roll back to previous configurations if necessary.<\/li>\n<li><strong>Template Free:<\/strong> Kustomize is template free. It expresses the full power of Kubernetes API, with no need to parameterize every single line compared to Helm.<\/li>\n<li>Kustomize<strong> <\/strong>can be run natively from the Kubernetes command line interface.<\/li>\n<li>Kustomize has built-in transformers to modify resources and It can be extended via a plug-in mechanism.<\/li>\n<li>Kustomize does not have any templating language so we can use the usual YAML to state our configurations rapidly.<\/li>\n<li>Kustomize is provided as a standalone Golang package and cli tool so it&#8217;s easy to integrate with users&#8217; tools and workflows.<\/li>\n<li>We can use Kustomize without installing it if we have kubectl 1.14+ version. kubectl allows us to make declarative changes to our configurations without touching a template.<\/li>\n<\/ol>\n<h2 id=\"kustomize-best-practices\">Kustomize Best Practices<\/h2>\n<p>Here are some Kustomize best practices:<\/p>\n<ol>\n<li>Keeping base resources, overlays, and patches in separate directories helps us to maintain clarity and separation between different configurations.<\/li>\n<li>While working with Kustomize, it&#8217;s essential to adhere to general Kubernetes best practices<\/li>\n<li>Try to keep the common values like namespace, and common metadata in the base file.<\/li>\n<li>While developing or before pushing to git, run <code>kubectl kustomize cfg fmt file_name<\/code>  command to format the file and set the indentation right.<\/li>\n<li>Before deploying your Kustomize configurations, validate them and perform thorough testing to ensure that your configurations work as expected.<\/li>\n<li>Integrate Kustomize into your CI\/CD (Continuous Integration\/Continuous Deployment) pipeline to automate the deployment process.<\/li>\n<li>Kustomize provides the ability to imperatively update <code>kustomization.yaml<\/code> files using the edit subcommand. So use the edit command in adding labels, namespaces, etc, and modify images and replicas.<\/li>\n<\/ol>\n<h2 id=\"kustomize-vs-helm\">Kustomize vs Helm<\/h2>\n<p>Below are some differences:<\/p>\n<ol>\n<li>Helm provides more advanced features like hooks and release management, making it suitable for complex deployments. Kustomize is simpler and more straightforward.<\/li>\n<li>Helm has complex templating, while Kustomize has no templating.<\/li>\n<li>Kustomize doesn&#8217;t need a separate setup. On the other hand, we need to set up Helm.<\/li>\n<li>Helm has a large collection of pre-built charts that can be easily shared and reused, while Kustomize allows you to share configurations but lacks a centralized repository.<\/li>\n<li>Kustomize has an easy learning curve, while Helm has difficulty as it introduces additional concepts like charts and templates.<\/li>\n<li>Kustomize uses overlays and patches to modify existing configurations, while Helm uses charts to package and manage applications.<\/li>\n<\/ol>\n<h2 id=\"kustomize-faqs\">Kustomize FAQs<\/h2>\n<p>Let\u2019s look at some of the frequently asked Kutomize questions.<\/p>\n<h4 id=\"how-is-kustomize-different-from-helm\">How is Kustomize different from Helm?<\/h4>\n<p>While both Kustomize and Helm are configuration management tools for Kubernetes, they have different approaches. Kustomize focuses on providing a native and declarative way to manage configurations without using templates. Helm, on the other hand, uses charts and templates to package and deploy applications.<\/p>\n<h4 id=\"how-do-i-install-kustomize\">How do I install Kustomize?<\/h4>\n<p>Kustomize is distributed as a standalone binary and can be easily installed on various platforms.<\/p>\n<pre><code>curl -s \"https:\/\/raw.githubusercontent.com\/kubernetes-sigs\/kustomize\/master\/hack\/install_kustomize.sh\"  | bash<\/code><\/pre>\n<p>You can download the binary from the official GitHub repository or use package managers like Homebrew (for macOS\/Linux) or Chocolatey (for Windows).<\/p>\n<pre><code>brew install kustomize    \/\/mac\nchoco install kustomize   \/\/windows<\/code><\/pre>\n<h4 id=\"can-kustomize-be-used-with-existing-kubernetes-configurations\">Can Kustomize be used with existing Kubernetes configurations?<\/h4>\n<p>Kustomize can be used to manage existing Kubernetes configurations. You can start by creating a Kustomization file in the directory containing your existing YAML files and define overlays to customize the configuration as needed.<\/p>\n<h4 id=\"can-kustomize-handle-secret-management\">Can Kustomize handle secret management?<\/h4>\n<p>Kustomize provides support for managing secrets. You can define secret generators in your Kustomization file, which can create secrets dynamically based on the specified rules. This allows you to separate sensitive information from your configuration files.<\/p>\n<h4 id=\"is-kustomize-compatible-with-gitops-workflows\">Is Kustomize compatible with GitOps workflows?<\/h4>\n<p>Kustomize is compatible with GitOps workflows. You can version control your base configuration, overlays, and Kustomization files and use tools like Git and CI\/CD pipelines to manage and deploy your Kubernetes configurations.<\/p>\n<h4 id=\"is-kustomize-officially-supported-by-kubernetes\">Is Kustomize officially supported by Kubernetes?<\/h4>\n<p>Kustomize is an official sub-project of Kubernetes and is maintained by the Kubernetes SIG-CLI (Special Interest Group &#8211; Command Line Interface) community. It has gained popularity and is widely used in the Kubernetes ecosystem.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>In this Kustomize Tutorial, we have discussed what problem Kustomize solves along with its benefits. Further, we have checked the installation part and some key concepts of Kustomize.<\/p>\n<p>We also learned how to deploy applications on Kubernetes using Kustomize. Then we talked about the Secret\/Config Map generator and compared it with Helm.<\/p>\n<p>The main advantage of Kustomize is that it is very easy to start using as it&#8217;s integrated with <code>kubectl<\/code> as well.<\/p>\n<p>If you are learning Kubernetes check out the <a href=\"https:\/\/devopscube.com\/learn-kubernetes-complete-roadmap\/\" rel=\"noreferrer noopener\">Kubernetes learning roadmap<\/a> to deepen your Kubernetes knowledge.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/kustomize-tutorial\/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes Kustomize Tutorial (Comprehensive Guide) \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/kustomize-tutorial\/<\/p>\n","protected":false},"author":1,"featured_media":945,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-944","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\/944","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=944"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/944\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/945"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=944"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=944"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=944"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}