[{"data":1,"prerenderedAt":1199},["ShallowReactive",2],{"navigation":3,"\u002Fen\u002Fblog\u002Fwhat-is-a-helm-chart":4,"\u002Fen\u002Fblog\u002Fwhat-is-a-helm-chart-surround":1188},[],{"id":5,"title":6,"authors":7,"badge":13,"body":14,"date":1177,"description":1178,"extension":1179,"image":1180,"lastUpdated":1182,"meta":1183,"navigation":196,"path":1184,"published":196,"seo":1185,"stem":1186,"tags":13,"__hash__":1187},"posts\u002Fen\u002F3.blog\u002F57.what-is-a-helm-chart.md","What Is a Helm Chart? The Package Manager for Kubernetes",[8],{"name":9,"to":10,"avatar":11},"Thomas Ens","\u002Fabout\u002Fthomasens",{"src":12},"\u002Fimages\u002Fblog\u002Fauthors\u002Fthomas.jpeg",null,{"type":15,"value":16,"toc":1163},"minimark",[17,27,30,35,44,47,50,54,57,68,73,76,162,170,174,177,274,277,281,284,436,451,455,458,461,525,528,631,642,649,653,656,844,847,851,865,868,909,916,920,923,937,940,946,1012,1019,1023,1031,1038,1041,1044,1060,1063,1067,1074,1131,1138,1141,1144,1151,1159],[18,19,20,21,26],"p",{},"Anyone running ",[22,23,25],"a",{"href":24},"\u002Fen\u002Fblog\u002Fwhat-is-kubernetes","Kubernetes"," seriously knows the problem: an application consists of a Deployment, a Service, a ConfigMap, maybe a HorizontalPodAutoscaler, and an Ingress. That's five YAML files — per environment. And you already have a management problem. Helm charts are the standard answer to this complexity. They bundle all Kubernetes resources of an application into a versioned, configurable package.",[18,28,29],{},"This article explains how Helm charts are structured, how templating works, and when it makes sense to use them.",[31,32,34],"h2",{"id":33},"what-is-helm-and-what-is-a-chart","What is Helm and what is a chart?",[18,36,37,43],{},[22,38,42],{"href":39,"rel":40},"https:\u002F\u002Fhelm.sh\u002F",[41],"nofollow","Helm"," is the package manager for Kubernetes. If you think of apt for Debian, Homebrew for macOS, or npm for Node.js, you're conceptually close. Helm installs, updates, and uninstalls applications on a Kubernetes cluster while keeping track of the state of each installation.",[18,45,46],{},"A Helm chart is the package format Helm uses. At its core, it's a collection of files in a defined directory structure that together describe what should be deployed to the cluster, parameterised via a central configuration file.",[18,48,49],{},"The key advantage over raw YAML: instead of maintaining separate copies for each environment, you define the structure once and adjust what differs via a values file or CLI parameters.",[31,51,53],{"id":52},"structure-of-a-helm-chart","Structure of a Helm chart",[18,55,56],{},"A freshly created chart looks like this:",[58,59,64],"pre",{"className":60,"code":62,"language":63},[61],"language-text","my-app\u002F\n├── Chart.yaml\n├── values.yaml\n├── charts\u002F\n└── templates\u002F\n    ├── deployment.yaml\n    ├── service.yaml\n    ├── _helpers.tpl\n    └── NOTES.txt\n","text",[65,66,62],"code",{"__ignoreMap":67},"",[69,70,72],"h3",{"id":71},"chartyaml","Chart.yaml",[18,74,75],{},"This file contains the chart's metadata:",[58,77,81],{"className":78,"code":79,"language":80,"meta":67,"style":67},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","apiVersion: v2\nname: my-app\ndescription: A Helm chart for my application\ntype: application\nversion: 0.1.0\nappVersion: \"1.2.3\"\n","yaml",[65,82,83,100,111,122,133,145],{"__ignoreMap":67},[84,85,88,92,96],"span",{"class":86,"line":87},"line",1,[84,89,91],{"class":90},"swJcz","apiVersion",[84,93,95],{"class":94},"sMK4o",":",[84,97,99],{"class":98},"sfazB"," v2\n",[84,101,103,106,108],{"class":86,"line":102},2,[84,104,105],{"class":90},"name",[84,107,95],{"class":94},[84,109,110],{"class":98}," my-app\n",[84,112,114,117,119],{"class":86,"line":113},3,[84,115,116],{"class":90},"description",[84,118,95],{"class":94},[84,120,121],{"class":98}," A Helm chart for my application\n",[84,123,125,128,130],{"class":86,"line":124},4,[84,126,127],{"class":90},"type",[84,129,95],{"class":94},[84,131,132],{"class":98}," application\n",[84,134,136,139,141],{"class":86,"line":135},5,[84,137,138],{"class":90},"version",[84,140,95],{"class":94},[84,142,144],{"class":143},"sbssI"," 0.1.0\n",[84,146,148,151,153,156,159],{"class":86,"line":147},6,[84,149,150],{"class":90},"appVersion",[84,152,95],{"class":94},[84,154,155],{"class":94}," \"",[84,157,158],{"class":98},"1.2.3",[84,160,161],{"class":94},"\"\n",[18,163,164,166,167,169],{},[65,165,138],{}," is the version of the chart itself — that is, of the package. ",[65,168,150],{}," describes the version of the application being deployed. The two are independent and should be versioned separately.",[69,171,173],{"id":172},"valuesyaml","values.yaml",[18,175,176],{},"This is where the default values for all configurable parameters live:",[58,178,180],{"className":78,"code":179,"language":80,"meta":67,"style":67},"replicaCount: 2\n\nimage:\n  repository: my-registry\u002Fmy-app\n  tag: \"1.2.3\"\n  pullPolicy: IfNotPresent\n\nservice:\n  type: ClusterIP\n  port: 80\n",[65,181,182,192,198,206,216,229,239,244,252,263],{"__ignoreMap":67},[84,183,184,187,189],{"class":86,"line":87},[84,185,186],{"class":90},"replicaCount",[84,188,95],{"class":94},[84,190,191],{"class":143}," 2\n",[84,193,194],{"class":86,"line":102},[84,195,197],{"emptyLinePlaceholder":196},true,"\n",[84,199,200,203],{"class":86,"line":113},[84,201,202],{"class":90},"image",[84,204,205],{"class":94},":\n",[84,207,208,211,213],{"class":86,"line":124},[84,209,210],{"class":90},"  repository",[84,212,95],{"class":94},[84,214,215],{"class":98}," my-registry\u002Fmy-app\n",[84,217,218,221,223,225,227],{"class":86,"line":135},[84,219,220],{"class":90},"  tag",[84,222,95],{"class":94},[84,224,155],{"class":94},[84,226,158],{"class":98},[84,228,161],{"class":94},[84,230,231,234,236],{"class":86,"line":147},[84,232,233],{"class":90},"  pullPolicy",[84,235,95],{"class":94},[84,237,238],{"class":98}," IfNotPresent\n",[84,240,242],{"class":86,"line":241},7,[84,243,197],{"emptyLinePlaceholder":196},[84,245,247,250],{"class":86,"line":246},8,[84,248,249],{"class":90},"service",[84,251,205],{"class":94},[84,253,255,258,260],{"class":86,"line":254},9,[84,256,257],{"class":90},"  type",[84,259,95],{"class":94},[84,261,262],{"class":98}," ClusterIP\n",[84,264,266,269,271],{"class":86,"line":265},10,[84,267,268],{"class":90},"  port",[84,270,95],{"class":94},[84,272,273],{"class":143}," 80\n",[18,275,276],{},"These values are the control point for everything that differs between environments. If you need different replica counts or a different image tag for production, you only override this file — the templates stay unchanged.",[69,278,280],{"id":279},"the-templates-directory","The templates\u002F directory",[18,282,283],{},"This is where the actual Kubernetes manifests live — but with placeholders instead of fixed values:",[58,285,287],{"className":78,"code":286,"language":80,"meta":67,"style":67},"apiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: {{ include \"my-app.fullname\" . }}\nspec:\n  replicas: {{ .Values.replicaCount }}\n  selector:\n    matchLabels:\n      {{- include \"my-app.selectorLabels\" . | nindent 6 }}\n  template:\n    spec:\n      containers:\n        - name: {{ .Chart.Name }}\n          image: \"{{ .Values.image.repository }}:{{ .Values.image.tag }}\"\n",[65,288,289,298,308,315,331,338,352,359,366,380,387,395,403,421],{"__ignoreMap":67},[84,290,291,293,295],{"class":86,"line":87},[84,292,91],{"class":90},[84,294,95],{"class":94},[84,296,297],{"class":98}," apps\u002Fv1\n",[84,299,300,303,305],{"class":86,"line":102},[84,301,302],{"class":90},"kind",[84,304,95],{"class":94},[84,306,307],{"class":98}," Deployment\n",[84,309,310,313],{"class":86,"line":113},[84,311,312],{"class":90},"metadata",[84,314,205],{"class":94},[84,316,317,320,322,325,328],{"class":86,"line":124},[84,318,319],{"class":90},"  name",[84,321,95],{"class":94},[84,323,324],{"class":94}," {{",[84,326,327],{"class":98}," include \"my-app.fullname\" .",[84,329,330],{"class":94}," }}\n",[84,332,333,336],{"class":86,"line":135},[84,334,335],{"class":90},"spec",[84,337,205],{"class":94},[84,339,340,343,345,347,350],{"class":86,"line":147},[84,341,342],{"class":90},"  replicas",[84,344,95],{"class":94},[84,346,324],{"class":94},[84,348,349],{"class":98}," .Values.replicaCount",[84,351,330],{"class":94},[84,353,354,357],{"class":86,"line":241},[84,355,356],{"class":90},"  selector",[84,358,205],{"class":94},[84,360,361,364],{"class":86,"line":246},[84,362,363],{"class":90},"    matchLabels",[84,365,205],{"class":94},[84,367,368,371,375,378],{"class":86,"line":254},[84,369,370],{"class":94},"      {{",[84,372,374],{"class":373},"sTEyZ","- ",[84,376,377],{"class":98},"include \"my-app.selectorLabels\" . | nindent 6",[84,379,330],{"class":94},[84,381,382,385],{"class":86,"line":265},[84,383,384],{"class":90},"  template",[84,386,205],{"class":94},[84,388,390,393],{"class":86,"line":389},11,[84,391,392],{"class":90},"    spec",[84,394,205],{"class":94},[84,396,398,401],{"class":86,"line":397},12,[84,399,400],{"class":90},"      containers",[84,402,205],{"class":94},[84,404,406,409,412,414,416,419],{"class":86,"line":405},13,[84,407,408],{"class":94},"        -",[84,410,411],{"class":90}," name",[84,413,95],{"class":94},[84,415,324],{"class":94},[84,417,418],{"class":98}," .Chart.Name",[84,420,330],{"class":94},[84,422,424,427,429,431,434],{"class":86,"line":423},14,[84,425,426],{"class":90},"          image",[84,428,95],{"class":94},[84,430,155],{"class":94},[84,432,433],{"class":98},"{{ .Values.image.repository }}:{{ .Values.image.tag }}",[84,435,161],{"class":94},[18,437,438,439,442,443,446,447,450],{},"The double curly braces ",[65,440,441],{},"{{ }}"," are Go template syntax. ",[65,444,445],{},".Values"," accesses values.yaml, ",[65,448,449],{},".Chart"," accesses Chart.yaml.",[31,452,454],{"id":453},"how-helm-templating-works","How Helm templating works",[18,456,457],{},"Helm uses the Go template engine, extended with a range of additional functions from the Sprig library. This gives you conditionals, loops, string manipulation, and custom helper functions.",[18,459,460],{},"A simple example with a conditional:",[58,462,464],{"className":78,"code":463,"language":80,"meta":67,"style":67},"{{- if .Values.ingress.enabled }}\napiVersion: networking.k8s.io\u002Fv1\nkind: Ingress\nmetadata:\n  name: {{ include \"my-app.fullname\" . }}\n{{- end }}\n",[65,465,466,478,487,496,502,514],{"__ignoreMap":67},[84,467,468,471,473,476],{"class":86,"line":87},[84,469,470],{"class":94},"{{",[84,472,374],{"class":373},[84,474,475],{"class":98},"if .Values.ingress.enabled",[84,477,330],{"class":94},[84,479,480,482,484],{"class":86,"line":102},[84,481,91],{"class":90},[84,483,95],{"class":94},[84,485,486],{"class":98}," networking.k8s.io\u002Fv1\n",[84,488,489,491,493],{"class":86,"line":113},[84,490,302],{"class":90},[84,492,95],{"class":94},[84,494,495],{"class":98}," Ingress\n",[84,497,498,500],{"class":86,"line":124},[84,499,312],{"class":90},[84,501,205],{"class":94},[84,503,504,506,508,510,512],{"class":86,"line":135},[84,505,319],{"class":90},[84,507,95],{"class":94},[84,509,324],{"class":94},[84,511,327],{"class":98},[84,513,330],{"class":94},[84,515,516,518,520,523],{"class":86,"line":147},[84,517,470],{"class":94},[84,519,374],{"class":373},[84,521,522],{"class":98},"end",[84,524,330],{"class":94},[18,526,527],{},"Or a loop over multiple hosts:",[58,529,531],{"className":78,"code":530,"language":80,"meta":67,"style":67},"rules:\n  {{- range .Values.ingress.hosts }}\n  - host: {{ .host | quote }}\n    http:\n      paths:\n        {{- range .paths }}\n        - path: {{ .path }}\n        {{- end }}\n  {{- end }}\n",[65,532,533,540,552,569,576,583,595,611,621],{"__ignoreMap":67},[84,534,535,538],{"class":86,"line":87},[84,536,537],{"class":90},"rules",[84,539,205],{"class":94},[84,541,542,545,547,550],{"class":86,"line":102},[84,543,544],{"class":94},"  {{",[84,546,374],{"class":373},[84,548,549],{"class":98},"range .Values.ingress.hosts",[84,551,330],{"class":94},[84,553,554,557,560,562,564,567],{"class":86,"line":113},[84,555,556],{"class":94},"  -",[84,558,559],{"class":90}," host",[84,561,95],{"class":94},[84,563,324],{"class":94},[84,565,566],{"class":98}," .host | quote",[84,568,330],{"class":94},[84,570,571,574],{"class":86,"line":124},[84,572,573],{"class":90},"    http",[84,575,205],{"class":94},[84,577,578,581],{"class":86,"line":135},[84,579,580],{"class":90},"      paths",[84,582,205],{"class":94},[84,584,585,588,590,593],{"class":86,"line":147},[84,586,587],{"class":94},"        {{",[84,589,374],{"class":373},[84,591,592],{"class":98},"range .paths",[84,594,330],{"class":94},[84,596,597,599,602,604,606,609],{"class":86,"line":241},[84,598,408],{"class":94},[84,600,601],{"class":90}," path",[84,603,95],{"class":94},[84,605,324],{"class":94},[84,607,608],{"class":98}," .path",[84,610,330],{"class":94},[84,612,613,615,617,619],{"class":86,"line":246},[84,614,587],{"class":94},[84,616,374],{"class":373},[84,618,522],{"class":98},[84,620,330],{"class":94},[84,622,623,625,627,629],{"class":86,"line":254},[84,624,544],{"class":94},[84,626,374],{"class":373},[84,628,522],{"class":98},[84,630,330],{"class":94},[18,632,633,634,637,638,641],{},"Custom helper functions live in ",[65,635,636],{},"_helpers.tpl",". The file starts with an underscore so Helm doesn't treat it as a standalone manifest. There you define named templates that you pull into the other files via ",[65,639,640],{},"include",".",[18,643,644,645,648],{},"With ",[65,646,647],{},"helm template my-app .\u002Fmy-app"," you can render the YAML locally — useful for debugging before you let anything loose on the cluster.",[31,650,652],{"id":651},"helm-commands-for-daily-use","Helm commands for daily use",[18,654,655],{},"The most important operations:",[58,657,661],{"className":658,"code":659,"language":660,"meta":67,"style":67},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Install a chart\nhelm install my-release .\u002Fmy-app\n\n# Override with custom values\nhelm install my-release .\u002Fmy-app -f production-values.yaml\n\n# Upgrade an existing installation\nhelm upgrade my-release .\u002Fmy-app --set image.tag=1.3.0\n\n# Install or upgrade (idempotent — good for CI)\nhelm upgrade --install my-release .\u002Fmy-app\n\n# Show status of an installation\nhelm status my-release\n\n# List all releases\nhelm list\n\n# Rollback to an earlier version\nhelm rollback my-release 1\n\n# Uninstall\nhelm uninstall my-release\n","bash",[65,662,663,669,684,688,693,710,714,719,739,743,748,761,765,770,780,785,791,799,804,810,823,828,834],{"__ignoreMap":67},[84,664,665],{"class":86,"line":87},[84,666,668],{"class":667},"sHwdD","# Install a chart\n",[84,670,671,675,678,681],{"class":86,"line":102},[84,672,674],{"class":673},"sBMFI","helm",[84,676,677],{"class":98}," install",[84,679,680],{"class":98}," my-release",[84,682,683],{"class":98}," .\u002Fmy-app\n",[84,685,686],{"class":86,"line":113},[84,687,197],{"emptyLinePlaceholder":196},[84,689,690],{"class":86,"line":124},[84,691,692],{"class":667},"# Override with custom values\n",[84,694,695,697,699,701,704,707],{"class":86,"line":135},[84,696,674],{"class":673},[84,698,677],{"class":98},[84,700,680],{"class":98},[84,702,703],{"class":98}," .\u002Fmy-app",[84,705,706],{"class":98}," -f",[84,708,709],{"class":98}," production-values.yaml\n",[84,711,712],{"class":86,"line":147},[84,713,197],{"emptyLinePlaceholder":196},[84,715,716],{"class":86,"line":241},[84,717,718],{"class":667},"# Upgrade an existing installation\n",[84,720,721,723,726,728,730,733,736],{"class":86,"line":246},[84,722,674],{"class":673},[84,724,725],{"class":98}," upgrade",[84,727,680],{"class":98},[84,729,703],{"class":98},[84,731,732],{"class":98}," --set",[84,734,735],{"class":98}," image.tag=",[84,737,738],{"class":143},"1.3.0\n",[84,740,741],{"class":86,"line":254},[84,742,197],{"emptyLinePlaceholder":196},[84,744,745],{"class":86,"line":265},[84,746,747],{"class":667},"# Install or upgrade (idempotent — good for CI)\n",[84,749,750,752,754,757,759],{"class":86,"line":389},[84,751,674],{"class":673},[84,753,725],{"class":98},[84,755,756],{"class":98}," --install",[84,758,680],{"class":98},[84,760,683],{"class":98},[84,762,763],{"class":86,"line":397},[84,764,197],{"emptyLinePlaceholder":196},[84,766,767],{"class":86,"line":405},[84,768,769],{"class":667},"# Show status of an installation\n",[84,771,772,774,777],{"class":86,"line":423},[84,773,674],{"class":673},[84,775,776],{"class":98}," status",[84,778,779],{"class":98}," my-release\n",[84,781,783],{"class":86,"line":782},15,[84,784,197],{"emptyLinePlaceholder":196},[84,786,788],{"class":86,"line":787},16,[84,789,790],{"class":667},"# List all releases\n",[84,792,794,796],{"class":86,"line":793},17,[84,795,674],{"class":673},[84,797,798],{"class":98}," list\n",[84,800,802],{"class":86,"line":801},18,[84,803,197],{"emptyLinePlaceholder":196},[84,805,807],{"class":86,"line":806},19,[84,808,809],{"class":667},"# Rollback to an earlier version\n",[84,811,813,815,818,820],{"class":86,"line":812},20,[84,814,674],{"class":673},[84,816,817],{"class":98}," rollback",[84,819,680],{"class":98},[84,821,822],{"class":143}," 1\n",[84,824,826],{"class":86,"line":825},21,[84,827,197],{"emptyLinePlaceholder":196},[84,829,831],{"class":86,"line":830},22,[84,832,833],{"class":667},"# Uninstall\n",[84,835,837,839,842],{"class":86,"line":836},23,[84,838,674],{"class":673},[84,840,841],{"class":98}," uninstall",[84,843,779],{"class":98},[18,845,846],{},"Helm stores the state of each installation as a release in the cluster (as a Secret or ConfigMap) and manages revisions. That allows rollback to an earlier version with a single command.",[31,848,850],{"id":849},"chart-repositories-and-artifact-hub","Chart repositories and Artifact Hub",[18,852,853,854,859,860,864],{},"Ready-made charts for common software can be found on ",[22,855,858],{"href":856,"rel":857},"https:\u002F\u002Fartifacthub.io",[41],"Artifact Hub",", the central directory for public Helm charts. There are charts for cert-manager, ingress-nginx, Prometheus, ",[22,861,863],{"href":862},"\u002Fen\u002Fblog\u002Fpostgresql-helm-chart-kubernetes","PostgreSQL",", and hundreds more.",[18,866,867],{},"Adding a repository and installing from it:",[58,869,871],{"className":658,"code":870,"language":660,"meta":67,"style":67},"helm repo add ingress-nginx https:\u002F\u002Fkubernetes.github.io\u002Fingress-nginx\nhelm repo update\nhelm install ingress-nginx ingress-nginx\u002Fingress-nginx\n",[65,872,873,889,898],{"__ignoreMap":67},[84,874,875,877,880,883,886],{"class":86,"line":87},[84,876,674],{"class":673},[84,878,879],{"class":98}," repo",[84,881,882],{"class":98}," add",[84,884,885],{"class":98}," ingress-nginx",[84,887,888],{"class":98}," https:\u002F\u002Fkubernetes.github.io\u002Fingress-nginx\n",[84,890,891,893,895],{"class":86,"line":102},[84,892,674],{"class":673},[84,894,879],{"class":98},[84,896,897],{"class":98}," update\n",[84,899,900,902,904,906],{"class":86,"line":113},[84,901,674],{"class":673},[84,903,677],{"class":98},[84,905,885],{"class":98},[84,907,908],{"class":98}," ingress-nginx\u002Fingress-nginx\n",[18,910,911,912,915],{},"For your own charts, you can run a private repository — classically as a static HTTP server with an ",[65,913,914],{},"index.yaml",", or in a more modern way as an OCI registry. Since Helm 3.8, OCI support is stable, and many teams use their existing container registry for both charts and images.",[31,917,919],{"id":918},"creating-a-helm-chart-deploying-your-own-application","Creating a Helm chart. Deploying your own application",[18,921,922],{},"Creating a new chart:",[58,924,926],{"className":658,"code":925,"language":660,"meta":67,"style":67},"helm create my-app\n",[65,927,928],{"__ignoreMap":67},[84,929,930,932,935],{"class":86,"line":87},[84,931,674],{"class":673},[84,933,934],{"class":98}," create",[84,936,110],{"class":98},[18,938,939],{},"This generates the full directory structure with example templates for a Deployment including Service, Ingress, HPA, and ServiceAccount. The generated code is functional and a good starting point.",[18,941,942,943,945],{},"From there you adapt ",[65,944,173],{}," to your application and clean up the templates by removing anything you don't need. You can test this with:",[58,947,949],{"className":658,"code":948,"language":660,"meta":67,"style":67},"# Check rendering\nhelm template my-release .\u002Fmy-app -f my-values.yaml\n\n# Lint check\nhelm lint .\u002Fmy-app\n\n# Dry-run against the cluster\nhelm install my-release .\u002Fmy-app --dry-run\n",[65,950,951,956,972,976,981,990,994,999],{"__ignoreMap":67},[84,952,953],{"class":86,"line":87},[84,954,955],{"class":667},"# Check rendering\n",[84,957,958,960,963,965,967,969],{"class":86,"line":102},[84,959,674],{"class":673},[84,961,962],{"class":98}," template",[84,964,680],{"class":98},[84,966,703],{"class":98},[84,968,706],{"class":98},[84,970,971],{"class":98}," my-values.yaml\n",[84,973,974],{"class":86,"line":113},[84,975,197],{"emptyLinePlaceholder":196},[84,977,978],{"class":86,"line":124},[84,979,980],{"class":667},"# Lint check\n",[84,982,983,985,988],{"class":86,"line":135},[84,984,674],{"class":673},[84,986,987],{"class":98}," lint",[84,989,683],{"class":98},[84,991,992],{"class":86,"line":147},[84,993,197],{"emptyLinePlaceholder":196},[84,995,996],{"class":86,"line":241},[84,997,998],{"class":667},"# Dry-run against the cluster\n",[84,1000,1001,1003,1005,1007,1009],{"class":86,"line":246},[84,1002,674],{"class":673},[84,1004,677],{"class":98},[84,1006,680],{"class":98},[84,1008,703],{"class":98},[84,1010,1011],{"class":98}," --dry-run\n",[18,1013,1014,1015,1018],{},"Before you run a chart in production, it's worth running ",[65,1016,1017],{},"helm lint",", which checks for common problems in chart structure and templates.",[31,1020,1022],{"id":1021},"helm-vs-kustomize-when-to-use-what","Helm vs. Kustomize. When to use what?",[18,1024,1025,1026,1030],{},"Both tools solve the ",[22,1027,1029],{"href":1028},"\u002Fen\u002Fblog\u002Fsimplify-kubernetes-configuration","YAML management problem in Kubernetes",", but with different approaches.",[18,1032,1033,1037],{},[22,1034,1036],{"href":1035},"\u002Fen\u002Fblog\u002Fwhat-is-kustomize","Kustomize"," works with overlays: you have a base configuration and layer environment-specific changes on top, without templates or variables. It's closer to plain YAML and doesn't introduce its own DSL. It's been built into kubectl since version 1.14.",[18,1039,1040],{},"Helm relies on templating and packaging. It's more powerful for complex configurations, but has a steeper learning curve and more moving parts.",[18,1042,1043],{},"Rules of thumb from practice:",[1045,1046,1047,1051,1054,1057],"ul",{},[1048,1049,1050],"li",{},"Deploying third-party software (cert-manager, Prometheus, etc.)? Helm, because the maintainers ship it as a chart.",[1048,1052,1053],{},"Managing your own apps across many environments with different configurations? Helm works well, but Kustomize is often clearer.",[1048,1055,1056],{},"Want to stay GitOps-friendly and prefer plain YAML? Kustomize.",[1048,1058,1059],{},"Need rollback functionality and release tracking? Helm.",[18,1061,1062],{},"Many teams combine both: Helm for installing external charts, Kustomize for their own application configuration.",[31,1064,1066],{"id":1065},"helm-in-cicd-and-gitops","Helm in CI\u002FCD and GitOps",[18,1068,1069,1070,1073],{},"In CI\u002FCD pipelines, ",[65,1071,1072],{},"helm upgrade --install"," is the standard command — it installs if nothing is there yet, and upgrades otherwise. This makes pipelines idempotent:",[58,1075,1077],{"className":658,"code":1076,"language":660,"meta":67,"style":67},"helm upgrade --install my-app .\u002Fchart \\\n  --namespace production \\\n  --create-namespace \\\n  --set image.tag=$CI_COMMIT_SHA \\\n  -f environments\u002Fproduction\u002Fvalues.yaml\n",[65,1078,1079,1096,1106,1113,1123],{"__ignoreMap":67},[84,1080,1081,1083,1085,1087,1090,1093],{"class":86,"line":87},[84,1082,674],{"class":673},[84,1084,725],{"class":98},[84,1086,756],{"class":98},[84,1088,1089],{"class":98}," my-app",[84,1091,1092],{"class":98}," .\u002Fchart",[84,1094,1095],{"class":373}," \\\n",[84,1097,1098,1101,1104],{"class":86,"line":102},[84,1099,1100],{"class":98},"  --namespace",[84,1102,1103],{"class":98}," production",[84,1105,1095],{"class":373},[84,1107,1108,1111],{"class":86,"line":113},[84,1109,1110],{"class":98},"  --create-namespace",[84,1112,1095],{"class":373},[84,1114,1115,1118,1120],{"class":86,"line":124},[84,1116,1117],{"class":98},"  --set",[84,1119,735],{"class":98},[84,1121,1122],{"class":373},"$CI_COMMIT_SHA \\\n",[84,1124,1125,1128],{"class":86,"line":135},[84,1126,1127],{"class":98},"  -f",[84,1129,1130],{"class":98}," environments\u002Fproduction\u002Fvalues.yaml\n",[18,1132,1133,1134,1137],{},"In GitOps setups with ArgoCD or Flux, Helm is natively supported. ArgoCD can point directly at chart repositories and manage Helm releases as Application resources. Flux brings its own ",[65,1135,1136],{},"HelmRelease"," custom resource type. The benefit: the desired state lives in the Git repository, and the cluster reconciles itself automatically.",[18,1139,1140],{},"For secrets in Helm there are several approaches: the Helm Secrets plugin (works with SOPS or Vault), External Secrets Operator, or a Secret Store CSI driver. Values with passwords or API keys don't belong in values.yaml unencrypted — that's one of the most common security mistakes in Helm use.",[1142,1143],"hr",{},[18,1145,1146,1147,1150],{},"Helm charts are today the de facto standard for structured Kubernetes deployments — for both standard software and your own applications. The learning curve is manageable: ",[65,1148,1149],{},"helm create"," generates a functional chart in seconds, and the concepts of templating and releases become clear quickly in practice.",[18,1152,1153,1154,1158],{},"Teams running Kubernetes on a platform with native Helm support get an extra benefit: ",[1155,1156,1157],"strong",{},"lowcloud"," offers a Kubernetes environment where Helm deployments just work, without the infrastructure overhead around them. That lets teams focus on chart content, not cluster management.",[1160,1161,1162],"style",{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":67,"searchDepth":102,"depth":102,"links":1164},[1165,1166,1171,1172,1173,1174,1175,1176],{"id":33,"depth":102,"text":34},{"id":52,"depth":102,"text":53,"children":1167},[1168,1169,1170],{"id":71,"depth":113,"text":72},{"id":172,"depth":113,"text":173},{"id":279,"depth":113,"text":280},{"id":453,"depth":102,"text":454},{"id":651,"depth":102,"text":652},{"id":849,"depth":102,"text":850},{"id":918,"depth":102,"text":919},{"id":1021,"depth":102,"text":1022},{"id":1065,"depth":102,"text":1066},"2026-04-08","Helm charts bundle Kubernetes resources into a versioned package. How they are structured, how templating works, and when to use them.","md",{"src":1181},"\u002Fimages\u002Fblog\u002Fwhat-is-a-helm-chart.jpg","2026-04-13",{},"\u002Fen\u002Fblog\u002Fwhat-is-a-helm-chart",{"title":6,"description":1178},"en\u002F3.blog\u002F57.what-is-a-helm-chart","eX_z4kan8JtubN2Pm6aW-a30DLy3LZy4UTSHhmJLr88",[1189,1194],{"title":1190,"path":1191,"stem":1192,"description":1193,"children":-1},"What is Docker Swarm? Container Orchestration Built In","\u002Fen\u002Fblog\u002Fwhat-is-docker-swarm","en\u002F3.blog\u002F56.what-is-docker-swarm","Docker Swarm explained: clusters, services, overlay networks, and how it compares to Kubernetes. When Swarm is the right choice for container orchestration.",{"title":1195,"path":1196,"stem":1197,"description":1198,"children":-1},"Docker vs Kubernetes: Compose, Swarm, and K8s Compared","\u002Fen\u002Fblog\u002Fdocker-vs-kubernetes","en\u002F3.blog\u002F58.docker-vs-kubernetes","Docker, Docker Compose, Docker Swarm, and Kubernetes head-to-head: which tool fits which problem, and when does it make sense to switch?",1776079521287]