A Helm chart is a packaging format for Kubernetes, acting as a blueprint to define, install, and upgrade complex applications. It bundles multiple YAML manifest files (templates) into a single unit, managed via Helm to enable easy versioning, sharing, and configuration reuse across environments.
Helm must be installed into your system. Below is the official script how to install it into your system.
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
dpasek@k0s1:~$curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 100 11929 100 11929 0 0 43973 0 100 11929 100 11929 0 0 43953 0 100 11929 100 11929 0 0 43950 0 0 Downloading https://get.helm.sh/helm-v3.20.2-linux-arm64.tar.gz Verifying checksum... Done. Preparing to install helm into /usr/local/bin helm installed into /usr/local/bin/helm dpasek@k0s1:~$
Now we can create our own simple Helm Charts and deploy it into 3-node Kubernetes cluster in consolidated architecture I have for playing with K8s. Let's host several applications (service) in our 3-node K8s cluster and observe how it behaves.
Application Hello
cd hello-nginx
cat > Chart.yaml <<EOF
apiVersion: v2
name: hello-nginx
description: Simple Hello World NGINX demo app
type: application
version: 0.1.0
appVersion: "1.0"
EOF
cat > values.yaml <<EOF
replicaCount: 2
image:
repository: nginxdemos/hello
tag: latest
pullPolicy: IfNotPresent
service:
type: NodePort
port: 80
EOF
cat > templates/deployment.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-hello-nginx
labels:
app: {{ .Release.Name }}-hello-nginx
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-hello-nginx
template:
metadata:
labels:
app: {{ .Release.Name }}-hello-nginx
spec:
containers:
- name: hello-nginx
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 80
EOF
cat > templates/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-hello-nginx
spec:
type: {{ .Values.service.type }}
selector:
app: {{ .Release.Name }}-hello-nginx
ports:
- port: {{ .Values.service.port }}
targetPort: 80
protocol: TCP
EOF
Now we can install application hello having all artifacts in directory ./hello-nginx
helm install hello ./hello-nginx
dpasek@k0s1:~$helm install hello ./hello-nginxNAME: hello LAST DEPLOYED: Mon May 11 11:34:34 2026 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None dpasek@k0s1:~$
The hello app is deployed.
Application Echo
cd echo-hashicorp
cat > Chart.yaml <<EOF
apiVersion: v2
name: echo-hashicorp
description: Simple Hashicorp HTTP Echo demo app
type: application
version: 0.1.0
appVersion: "1.0"
EOF
cat > values.yaml <<EOF
replicaCount: 3
image:
repository: hashicorp/http-echo
tag: latest
pullPolicy: IfNotPresent
service:
type: NodePort
port: 80
EOF
cat > templates/deployment.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-http-echo-hashicorp
labels:
app: {{ .Release.Name }}-http-echo-hashicorp
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-http-echo-hashicorp
template:
metadata:
labels:
app: {{ .Release.Name }}-http-echo-hashicorp
spec:
containers:
- name: http-echo-hashicorp
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 80
EOF
cat > templates/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-http-echo-hashicorp
spec:
type: {{ .Values.service.type }}
selector:
app: {{ .Release.Name }}-http-echo-hashicorp
ports:
- port: {{ .Values.service.port }}
targetPort: 80
protocol: TCP
EOF
Now we can install application Echo having all artifacts in directory ./echo-hashicorp
helm install echo ./echo-hashicorp
dpasek@k0s1:~$helm install echo ./echo-hashicorpNAME: echo LAST DEPLOYED: Mon May 11 14:27:18 2026 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None dpasek@k0s1:~$
Working with Kubectl
Two apps (Hello and Echo) are deployed on K8s cluster. Bellow are visible deployed theirs K8s pods across cluster nodes.
dpasek@k0s1:~$kubectl get pods -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES echo-http-echo-hashicorp-88b9f786c-7m62j 1/1 Running 0 9m58s 10.244.1.14 k0s2 <none> <none> echo-http-echo-hashicorp-88b9f786c-jf5w2 1/1 Running 0 9m58s 10.244.1.16 k0s2 <none> <none> echo-http-echo-hashicorp-88b9f786c-wtmfs 1/1 Running 0 9m58s 10.244.1.15 k0s2 <none> <none> hello-hello-nginx-55ff8f7fb9-h524n 1/1 Running 0 3h2m 10.244.1.12 k0s2 <none> <none> hello-hello-nginx-55ff8f7fb9-l82gw 0/1 Completed 0 3h2m 10.244.0.25 k0s1 <none> <none> hello-hello-nginx-55ff8f7fb9-qcgxq 1/1 Running 0 83m 10.244.1.13 k0s2 <none> <none> dpasek@k0s1:~$
The most useful K8s command is ... kubectl getl all ... because you can see all pods, services, deployments, and replicasets.
dpasek@k0s2:~$kubectl get allNAME READY STATUS RESTARTS AGE pod/echo-http-echo-hashicorp-88b9f786c-7m62j 1/1 Running 0 20m pod/echo-http-echo-hashicorp-88b9f786c-jf5w2 1/1 Running 0 20m pod/echo-http-echo-hashicorp-88b9f786c-wtmfs 1/1 Running 0 20m pod/hello-hello-nginx-55ff8f7fb9-h524n 1/1 Running 0 3h13m pod/hello-hello-nginx-55ff8f7fb9-l82gw 0/1 Completed 0 3h13m pod/hello-hello-nginx-55ff8f7fb9-qcgxq 1/1 Running 0 94m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/echo-http-echo-hashicorp NodePort 10.99.203.238 <none> 80:32677/TCP 20m service/hello-hello-nginx NodePort 10.110.90.212 <none> 80:31785/TCP 3h13m service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 45h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/echo-http-echo-hashicorp 3/3 3 3 20m deployment.apps/hello-hello-nginx 2/2 2 2 3h13m NAME DESIRED CURRENT READY AGE replicaset.apps/echo-http-echo-hashicorp-88b9f786c 3 3 3 20m replicaset.apps/hello-hello-nginx-55ff8f7fb9 2 2 2 3h13m dpasek@k0s2:~$
Recommended Kubectl Beginner Operational Commands
kubectl get pods
kubectl get all
kubectl logs -f <pod>
kubectl describe pod <pod>
kubectl exec -it <pod> -- sh
kubectl rollout restart deployment <deploy>
kubectl scale deployment <deploy> --replicas=3
Working with Helm
Operational procedures for Helm are usually described similarly to traditional enterprise application runbooks. The important difference is that helm procedures operate application releases,
not individual servers or VMs.
Recommended Helm Beginner Operational Commands
helm list
helm status <release>
helm upgrade <release> <chart>
helm rollback <release> <revision>
helm uninstall <release>
Helm install
In my small K8s lab I have my two helm charts stored on a home directory in single server (k0s1). This is not production ready, but good enough to test K8s behavior. Helm Repository Cache is enterprise ready solution, but too heavy for home lab. Anyway, let's do a testing with my local Helm Charts.
We already installed these two application charts - hello and echo. You can find procedures above in this blog post.
Helm list
We can list already installed applications.
dpasek@k0s1:~$helm listNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION echo default 1 2026-05-11 14:27:18.497102365 +0000 UTC deployed hi-nginx-0.1.0 1.0 hello default 1 2026-05-11 11:34:34.046517773 +0000 UTC deployed hello-nginx-0.1.0 1.0 dpasek@k0s1:~$
Helm status
We can check the status of these two applications.
dpasek@k0s3:/var/log$helm status helloNAME: hello LAST DEPLOYED: Mon May 11 11:34:34 2026 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None dpasek@k0s3:/var/log$
Helm uninstall
Of course, you can uninstall the application.
Helm get manifest
Helm get is another useful command. Especially get manifest. It can show the full application manifest.\
dpasek@k0s1:~$helm get manifest echo--- # Source: echo-hashicorp/templates/service.yaml apiVersion: v1 kind: Service metadata: name: echo-http-echo-hashicorp spec: type: NodePort selector: app: echo-http-echo-hashicorp ports: - port: 80 targetPort: 80 protocol: TCP --- # Source: echo-hashicorp/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: echo-http-echo-hashicorp labels: app: echo-http-echo-hashicorp spec: replicas: 3 selector: matchLabels: app: echo-http-echo-hashicorp template: metadata: labels: app: echo-http-echo-hashicorp spec: containers: - name: http-echo-hashicorp image: "hashicorp/http-echo:latest" imagePullPolicy: IfNotPresent ports: - containerPort: 80 dpasek@k0s1:~$
See full manifest is pretty useful during troubleshooting.
Conclusion
Most Important Kubernetes Operational Concept in Kubernetes is that Pods are cattle, not pets.
Meaning:
- Pods are disposable
- Kubernetes recreates them automatically
- you usually operate Deployments/Helm releases
- not individual Pods
That is one of the biggest mindset shifts from traditional VM operations.
In this article we deployed two applications - Hello (2 replicas) and Echo (3 replicas). These applications run as K8s Pods in K0s K8s distribution.
Hope someone find this insightful.
No comments:
Post a Comment