DEV Community

neil wu
neil wu

Posted on • Edited on

Helm Chart Tutorial with your local kubernates

Install Helm CLI

Follow the instruction at https://helm.sh/docs/intro/install/

sudo apt-get install curl gpg apt-transport-https --yes
curl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/helm.gpg] https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
Enter fullscreen mode Exit fullscreen mode

Artifact Hub

Artifact hub hosts publicly available helm chart to deploy the application to your kubernates.
https://artifacthub.io/

helm repo command

helm maintains a local cache of all the helm chart you downloaded, the command will add a helm chart info to your local cache

helm repo add bitnami https://charts.bitnami.com/bitnami 

Enter fullscreen mode Exit fullscreen mode

To list your local cache
helm repo list

To update your local cache
helm repo update

To search helm chart from your local(the cache is located $HOME/.cache/helm/repository
helm search repo wordpress

To search helm chart from artifacthub
helm search hub wordpress

To show a specific helm chart
helm show chart bitnami/wordpress

annotations:
  category: CMS
  images: |
    - name: apache-exporter
      image: docker.io/bitnami/apache-exporter:1.0.10-debian-12-r55
    - name: os-shell
      image: docker.io/bitnami/os-shell:12-debian-12-r50
    - name: wordpress
      image: docker.io/bitnami/wordpress:6.8.2-debian-12-r4
  licenses: Apache-2.0
  tanzuCategory: application
apiVersion: v2
appVersion: 6.8.2
dependencies:
- condition: memcached.enabled
  name: memcached
  repository: oci://registry-1.docker.io/bitnamicharts
  version: 7.x.x
- condition: mariadb.enabled
  name: mariadb
  repository: oci://registry-1.docker.io/bitnamicharts
  version: 22.x.x
- name: common
  repository: oci://registry-1.docker.io/bitnamicharts
  tags:
  - bitnami-common
  version: 2.x.x
description: WordPress is the world's most popular blogging and content management
  platform. Powerful yet simple, everyone from students to global corporations use
  it to build beautiful, functional websites.
home: https://bitnami.com
icon: https://dyltqmyl993wv.cloudfront.net/assets/stacks/wordpress/img/wordpress-stack-220x234.png
keywords:
- application
- blog
- cms
- http
- php
- web
- wordpress
maintainers:
- name: Broadcom, Inc. All Rights Reserved.
  url: https://github.com/bitnami/charts
name: wordpress
sources:
- https://github.com/bitnami/charts/tree/main/bitnami/wordpress
version: 26.0.0
Enter fullscreen mode Exit fullscreen mode

From the above detailed output of the show command about the helm chart, you can use the following keywords defined in the helm chart to do the search

keywords:
- application
- blog
- cms
- http
- php
- web
- wordpress

Enter fullscreen mode Exit fullscreen mode

helm search repo blog

NAME                    CHART VERSION   APP VERSION     DESCRIPTION
bitnami/ghost           25.0.4          6.0.5           Ghost is an open source publishing platform des...
bitnami/wordpress       26.0.0          6.8.2           WordPress is the world's most popular blogging ...
bitnami/wordpress-intel 2.1.31          6.1.1           DEPRECATED WordPress for Intel is the most popu...
bitnami/drupal          23.0.0          11.2.3          Drupal is one of the most versatile open source...
bitnami/joomla          20.0.4          5.1.2           DEPRECATED Joomla! is an award winning open sou...

Enter fullscreen mode Exit fullscreen mode

helm search repo will only lists the latest version of the chart if you do not specify --versions. To list all the versions.

neilwu@A8:~$ helm search repo wordpress --versions
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
bitnami/wordpress       26.0.0          6.8.2           WordPress is the world's most popular blogging ...
bitnami/wordpress       25.0.26         6.8.2           WordPress is the world's most popular blogging ...
bitnami/wordpress       25.0.25         6.8.2           WordPress is the world's most popular blogging ...
bitnami/wordpress       25.0.24         6.8.2           WordPress is the world's most popular blogging ...
bitnami/wordpress       25.0.23         6.8.2           WordPress is the world's most popular blogging ...
bitnami/wordpress       25.0.15         6.8.2           WordPress is the world's most popular blogging ...

Enter fullscreen mode Exit fullscreen mode

To remove a cached helm chart repo
helm repo remove bitnami

Installing wordpress helm chart in your local kubernates

  • Make sure your local kubernates is up and running
neilwu@A8:~$ kubectl version
Client Version: v1.32.2
Kustomize Version: v5.5.0
Server Version: v1.32.2
Enter fullscreen mode Exit fullscreen mode

If you see the above output, it means your local kubernates is up and running.

  • Make sure your current kubernates cluster is the current context The kubectl config current-context command is used to display the name of the Kubernetes context currently in use by kubectl. This context determines which Kubernetes cluster and user kubectl interacts with.
neilwu@A8:~$ kubectl config current-context
docker-desktop
Enter fullscreen mode Exit fullscreen mode
  • Install a helm chart

Command format
helm install [name] [helm chart name] --version=[version]
For example, to install wordpress helm chart

helm install my-wp binami/wordpress --version=24.1.18
neilwu@A8:~$ helm install my-wp binami/wordpress --version=24.1.18
NAME: my-wp
LAST DEPLOYED: Sun Sep 28 08:06:47 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: wordpress
CHART VERSION: 24.1.18
APP VERSION: 6.7.2

Enter fullscreen mode Exit fullscreen mode

Check the running pods

neilwu@A8:~$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
cassandra-6bfbff8548-qd8n7        1/1     Running   0          34h
my-wp-mariadb-0                   1/1     Running   0          78s
my-wp-wordpress-bffd47846-fgr8b   1/1     Running   0          78s
postgres-795cdc86bf-wkwvj         1/1     Running   0          18h

Enter fullscreen mode Exit fullscreen mode

Name prefixed with my-wp is all the pods with the installation.

Explore the installed helm chart : wordpress

Check the services

neilwu@A8:~$ kubectl get svc
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
cassandra-service        NodePort       10.97.53.48      <none>        9042:31082/TCP               33h
kubernetes               ClusterIP      10.96.0.1        <none>        443/TCP                      43h
my-wp-mariadb            ClusterIP      10.111.188.197   <none>        3306/TCP                     4m40s
my-wp-mariadb-headless   ClusterIP      None             <none>        3306/TCP                     4m40s
my-wp-wordpress          LoadBalancer   10.98.5.196      <pending>     80:31248/TCP,443:30684/TCP   4m40s
postgres                 ClusterIP      10.103.92.34     <none>        6432/TCP                     18h

Enter fullscreen mode Exit fullscreen mode

ClusterIP is the default Service type that provides a stable, internal, virtual IP address for a set of Pods within the cluster, allowing other applications to access the service without external exposure. It functions as a load balancer, distributing incoming traffic to the various Pods that match the service's label selector, ensuring reliable communication and enabling applications to connect to services using a consistent, internal endpoint

NodePort service provides external access to a Kubernetes service by opening a dedicated, high-numbered port on every node in the cluster, typically in the 30000-32767 range. Traffic sent to this port on any node is then forwarded by the kube-proxy to the service's internal port and then to one of its backend pod.

LoadBalancer is a hardware device or software application that distributes incoming network traffic and workloads across multiple servers, acting as an intermediary to prevent any single server from becoming overloaded. This distribution ensures maximum resource utilization, reduces response times, improves application availability and reliability, and provides seamless maintenance for servers and applications.

Check the secret

neilwu@A8:~$ kubectl get secret

NAME                             TYPE                 DATA   AGE
my-wp-mariadb                    Opaque               2      13m
my-wp-wordpress                  Opaque               1      13m
sh.helm.release.v1.my-wp.v1      helm.sh/release.v1   1      13m
sh.helm.release.v1.postgres.v1   helm.sh/release.v1   1      18h

neilwu@A8:~$ kubectl describe secret my-wp-wordpress
Name:         my-wp-wordpress
Namespace:    default
Labels:       app.kubernetes.io/instance=my-wp
              app.kubernetes.io/managed-by=Helm
              app.kubernetes.io/name=wordpress
              app.kubernetes.io/version=6.7.2
              helm.sh/chart=wordpress-24.1.18
Annotations:  meta.helm.sh/release-name: my-wp
              meta.helm.sh/release-namespace: default

Type:  Opaque

Data
====
wordpress-password:  10 bytes

Enter fullscreen mode Exit fullscreen mode

Open a NodePort on the cluster node

neilwu@A8:~$ kubectl get svc
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
cassandra-service        NodePort       10.97.53.48      <none>        9042:31082/TCP               37h
kubernetes               ClusterIP      10.96.0.1        <none>        443/TCP                      47h
my-wp-mariadb            ClusterIP      10.111.188.197   <none>        3306/TCP                     3h53m
my-wp-mariadb-headless   ClusterIP      None             <none>        3306/TCP                     3h53m
my-wp-wordpress          LoadBalancer   10.98.5.196      <pending>     80:31248/TCP,443:30684/TCP   3h53m
postgres                 ClusterIP      10.103.92.34     <none>        6432/TCP                     21h

Enter fullscreen mode Exit fullscreen mode
neilwu@A8:~$ kubectl get deployment
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
cassandra         1/1     1            1           39h
my-wp-wordpress   1/1     1            1           3h57m
postgres          1/1     1            1           21h

Enter fullscreen mode Exit fullscreen mode

Now expose the deployment as a service(a type of NodePort)

neilwu@A8:~$ kubectl expose deploy my-wp-wordpress --type=NodePort --name my-wp
service/my-wp exposed
neilwu@A8:~$ kubectl get svc
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
cassandra-service        NodePort       10.97.53.48      <none>        9042:31082/TCP                  37h
kubernetes               ClusterIP      10.96.0.1        <none>        443/TCP                         47h
my-wp                    NodePort       10.101.50.141    <none>        8080:32195/TCP,8443:30926/TCP   10s
my-wp-mariadb            ClusterIP      10.111.188.197   <none>        3306/TCP                        3h59m
my-wp-mariadb-headless   ClusterIP      None             <none>        3306/TCP                        3h59m
my-wp-wordpress          LoadBalancer   10.98.5.196      <pending>     80:31248/TCP,443:30684/TCP      3h59m
postgres                 ClusterIP      10.103.92.34     <none>        6432/TCP                        21h

Enter fullscreen mode Exit fullscreen mode

Now wordpress is exposed to your host machine

my-wp                    NodePort       10.101.50.141    <none>        8080:32195/TCP,8443:30926/TCP   10s

Enter fullscreen mode Exit fullscreen mode

You can use http://127.0.0.1:32195 to access wordpress home page hosted in a pod and http://localhost:32195/wp-admin is the admin console.

Explore the installed wordpress application

kubectl get secret

neilwu@A8:~$ kubectl get secret
NAME                             TYPE                 DATA   AGE
my-wp-mariadb                    Opaque               2      4h37m
my-wp-wordpress                  Opaque               1      4h37m

neilwu@A8:~$ kubectl describe secret my-wp-wordpress
Name:         my-wp-wordpress
Namespace:    default
Labels:       app.kubernetes.io/instance=my-wp
              app.kubernetes.io/managed-by=Helm
              app.kubernetes.io/name=wordpress
              app.kubernetes.io/version=6.7.2
              helm.sh/chart=wordpress-24.1.18
Annotations:  meta.helm.sh/release-name: my-wp
              meta.helm.sh/release-namespace: default

Type:  Opaque

Data
====
wordpress-password:  10 bytes
Enter fullscreen mode Exit fullscreen mode

wordpress-password is a 10 bytes long strings. Now it is time to decrypted

neilwu@A8:~$ kubectl get secret my-wp-wordpress -o jsonpath='{.data.wordpress-password}' | base64 -d
czrKG2tiamneilwu@A8:~$

Enter fullscreen mode Exit fullscreen mode

Eventually the username/password for the admin page http://localhost:32195/wp-admin will be

  • username user
  • password czrKG2tiam

Uninstalling your helm chart

neilwu@A8:~$ helm list
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
my-wp           default         1               2025-09-28 08:06:47.682168282 -0400 EDT deployed        wordpress-24.1.18       6.7.2
postgres        default         1               2025-09-27 14:07:17.321804979 -0400 EDT deployed        postgres-0.1.0          1.16.0

Enter fullscreen mode Exit fullscreen mode

Now uninstall my-wp

neilwu@A8:~$ helm uninstall my-wp
release "my-wp" uninstalled
neilwu@A8:~$ helm list
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
postgres        default         1               2025-09-27 14:07:17.321804979 -0400 EDT deployed        postgres-0.1.0  1.16.0
neilwu@A8:~$ kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
cassandra-6bfbff8548-qd8n7   1/1     Running   0          39h
postgres-795cdc86bf-wkwvj    1/1     Running   0          22h

Enter fullscreen mode Exit fullscreen mode

Now check services

neilwu@A8:~$ kubectl get svc
NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
cassandra-service   NodePort    10.97.53.48     <none>        9042:31082/TCP                  38h
kubernetes          ClusterIP   10.96.0.1       <none>        443/TCP                         2d
my-wp               NodePort    10.101.50.141   <none>        8080:32195/TCP,8443:30926/TCP   60m
postgres            ClusterIP   10.103.92.34    <none>        6432/TCP                        22h

Enter fullscreen mode Exit fullscreen mode

There is still one service left called my-wp which we created manually. We need to delete this manually created in the kubectl expose command.
To delete, issues kubectl delete svc my-wp

neilwu@A8:~$ kubectl delete svc my-wp
service "my-wp" deleted
neilwu@A8:~$ kubectl get svc
NAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
cassandra-service   NodePort    10.97.53.48    <none>        9042:31082/TCP   38h
kubernetes          ClusterIP   10.96.0.1      <none>        443/TCP          2d
postgres            ClusterIP   10.103.92.34   <none>        6432/TCP         23h

Enter fullscreen mode Exit fullscreen mode

To completely delete a helm chart, this is not the end. we also need to check if there are any pv(persistence volumn) and pvc(persistence volumn claim)

neilwu@A8:~$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                      STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-774e680d-e1a6-4a5b-8b3a-f8acc6c1b9a3   4Gi        RWO            Delete           Bound    default/postgres-persistent-volume-claim   hostpath       <unset>                          23h
pvc-ba1c49f0-bdca-4da1-bbf0-7d00bc1d2579   8Gi        RWO            Delete           Bound    default/data-my-wp-mariadb-0               hostpath       <unset>                          5h5m
neilwu@A8:~$ kubectl get pvc
NAME                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
data-my-wp-mariadb-0               Bound    pvc-ba1c49f0-bdca-4da1-bbf0-7d00bc1d2579   8Gi        RWO            hostpath       <unset>                 5h6m
postgres-persistent-volume-claim   Bound    pvc-774e680d-e1a6-4a5b-8b3a-f8acc6c1b9a3   4Gi        RWO            hostpath       <unset>                 23h

Enter fullscreen mode Exit fullscreen mode

The output shows data-my-wp-mariadb-0 is the one to be deleted.
Before deletion, check kubernates storage class

neilwu@A8:~$ kubectl describe storageclass
Name:            hostpath
IsDefaultClass:  Yes
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"name":"hostpath"},"provisioner":"docker.io/hostpath","reclaimPolicy":"Delete","volumeBindingMode":"Immediate"}
,storageclass.kubernetes.io/is-default-class=true
Provisioner:           docker.io/hostpath
Parameters:            <none>
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete

Enter fullscreen mode Exit fullscreen mode

ReclaimPolicy is set to Delete. It mains the pv can be reclaimed by delete action.

neilwu@A8:~$ kubectl delete pvc data-my-wp-mariadb-0
persistentvolumeclaim "data-my-wp-mariadb-0" deleted
neilwu@A8:~$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                      STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-774e680d-e1a6-4a5b-8b3a-f8acc6c1b9a3   4Gi        RWO            Delete           Bound    default/postgres-persistent-volume-claim   hostpath       <unset>                          23h
neilwu@A8:~$ kubectl get pvc
NAME                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
postgres-persistent-volume-claim   Bound    pvc-774e680d-e1a6-4a5b-8b3a-f8acc6c1b9a3   4Gi        RWO            hostpath       <unset>                 23h

Enter fullscreen mode Exit fullscreen mode

Top comments (0)