Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Building a Minimal Helm Chart from a Deployment and Service

Hello. In the last lesson, you deployed the frontend and backend with Kubernetes manifests and connected them through Ingress. Those manifests describe the right resources, but changing an image version or repeating the deployment in another environment still means editing YAML in several places.

This lesson turns the existing frontend Deployment and Service into a small Helm chart. You will keep their useful Kubernetes settings, move the image reference into values.yaml, and render the result locally. There is no AKS installation today: that avoids interfering with the frontend you may already have deployed. Allow about 40–50 minutes, including the readings.


What Helm adds to your existing manifests

A Helm chart is a directory containing chart metadata, default values, and templates that render into Kubernetes manifests. For this chart, Chart.yaml identifies the package; values.yaml supplies a default image reference; and two files under templates/ produce the Deployment and Service. Kubernetes still receives ordinary Deployment and Service objects—not a new kind of application workload.

Getting Started - Helm

Read Helm's “Getting Started” guide to see how a chart becomes Kubernetes YAML, and how a release name can appear in that YAML.

In “Charts,” examine the directory layout and read template rendering, including the following descriptions of values.yaml and Chart.yaml. In “A First Template,” find the ConfigMap example and read plain YAML templates. In “Adding a Simple Template Call,” start where the author discusses names unique to a release and read release names. Focus on what changes between a static manifest and a rendered one; you do not need to install the guide's example.

The chart’s metadata has a chart version, which identifies a version of the chart itself. That is distinct from the container image tag, which identifies the application image you want the Deployment to run.

Charts - Helm

Use Helm's “Charts” reference to check the required chart files and metadata before creating your own.

In “The Chart File Structure,” read from the directory explanation through the file-layout list. In “The Chart.yaml File,” inspect the required apiVersion, name, and version entries, then find “Charts and Versioning” and read chart versioning. In “The apiVersion Field,” note that v2 is the chart API version used here. You can skip the optional metadata fields and dependency sections.

For a first conversion, resist the temptation to parameterize every line. The aim is to preserve the frontend’s behavior and introduce only two Helm ideas: a value for its image and a release name for its Kubernetes resource names.


Create the minimal chart

Work in your application repository, alongside—not inside—your existing k8s/ manifests. If Helm is not installed locally, install the Helm CLI using your normal package-management process before running the validation commands below. Check that it is available with helm version --short.

Cost of this chart-building activity: creating files and running the Helm CLI locally creates no Azure resources and has no new Azure charge. Any AKS nodes, database, ACR storage, or networking resources you have left running continue to accrue their existing charges.

Create the directory:

mkdir -p charts/frontend-lab/templates

You could start with helm create, but it generates additional example templates that are easy to mistake for parts of your application. Creating three files and a directory makes the contents of this first chart explicit:

charts/frontend-lab/
├── Chart.yaml
├── values.yaml
└── templates/
    ├── deployment.yaml
    └── service.yaml

Create charts/frontend-lab/Chart.yaml:

apiVersion: v2
name: frontend-lab
description: Minimal chart for the application's React frontend
type: application
version: 0.1.0

Create charts/frontend-lab/values.yaml:

image:
  repository: replace-me.azurecr.io/frontend
  tag: "replace-with-your-version"

These are placeholders, not a public image or usable credentials. Before any eventual installation, substitute your private ACR repository and an image tag you have actually pushed. An image name is not a registry password; keep credentials and application secrets out of chart defaults.

Now convert the previous frontend Deployment into charts/frontend-lab/templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: frontend-lab
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: frontend-lab
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      containers:
        - name: frontend
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 250m
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 3
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 10
            periodSeconds: 10

Finally, create charts/frontend-lab/templates/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-frontend
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: frontend-lab
    app.kubernetes.io/instance: {{ .Release.Name }}
  ports:
    - name: http
      port: 80
      targetPort: http

The chart keeps the earlier port, ClusterIP exposure, resource settings, and health probes. Its Service selector matches both labels on the Pod template. Helm substitutes .Release.Name in both objects, so a release called ui-lab will render a Deployment and Service called ui-lab-frontend. Helm substitutes .Values.image.repository and .Values.image.tag from values.yaml into the image field.

Notice what is not in the templates: metadata.namespace. When the chart is eventually installed, the namespace is selected with Helm’s --namespace option. Keeping namespace out of these manifests avoids having one namespace in the files and another in the command.


Render and inspect before touching AKS

Rendering is the first useful feedback loop. It lets you inspect the exact Kubernetes YAML produced by the chart without pulling an image or changing the cluster.

Cost of these validation activities: helm lint and helm template run locally and create no new Azure charge. Existing Azure resources still incur their normal charges while they remain provisioned.

From the repository root, run:

helm lint ./charts/frontend-lab
helm template ui-lab ./charts/frontend-lab --namespace app-lab

In the rendered output, check the result rather than just looking for a successful exit code:

  • There should be one Deployment and one Service, both named ui-lab-frontend.
  • The Deployment’s Pod labels and the Service selector should agree.
  • The image should contain the repository and tag from values.yaml. With the supplied placeholders, it is deliberately not deployable.
  • The Service should remain ClusterIP on port 80, and the Deployment should retain both probes.

You can see why the release name belongs in the templates by rendering a second, hypothetical release:

helm template trial-ui ./charts/frontend-lab --namespace app-lab

Its resource names and instance labels will contain trial-ui instead. This command does not create that second release.

A successful render proves that Helm can generate YAML; it does not prove that the image exists, AKS can pull it, the Pods will become ready, or Ingress routes to the Service. Likewise, linting is a useful chart check, not a substitute for running the application.

Do not install this chart over the existing manually managed frontend. The previous lesson’s Ingress points to a Service named frontend, whereas this chart renders ui-lab-frontend; installing it unchanged would not move browser traffic to the chart’s Service. The older Deployment and Service would also remain separate resources. We will address configuration and the transition to a Helm-managed release deliberately in the following lessons rather than treating a successful render as a migration.


Where this chart goes next

You have built a minimal chart from a real Deployment and Service: Chart.yaml describes it, values.yaml supplies its image reference, and templates/ renders the Kubernetes objects. The important consistency check is that the Pod labels, Deployment selector, and Service selector still describe the same frontend after Helm fills in the release name.

Keep the existing manifests for comparison. Next, you will make the chart more adaptable—particularly its image, resources, application configuration, and ingress rules—without turning values.yaml into a place for secrets. Later, you will validate an installation and manage upgrades and rollbacks as a Helm release.

Can't find a good explanation? Sign up and we'll make it for you

Sign up