Client Area
KubernetesAdvanced

Helm Charts for Beginners — Deploying with Charts on k3s and Kubernetes

ByDomain India Team·DomainIndia Engineering
6 min readPublished 20 Apr 2026Updated 23 Jun 2026183 views

In this article

  • 1Why Helm
  • 2Install Helm
  • 3Pattern 1 — Install existing charts
  • 4Pattern 2 — Customise via values.yaml
  • 5Pattern 3 — Upgrade in place

Helm Charts for Beginners — Deploying with Charts on k3s and Kubernetes

TL;DR
Helm is the npm/apt of Kubernetes. Instead of writing 20 YAML files for a Postgres deployment, you install one Helm chart with a single command. This guide covers installing Helm, using existing charts, writing your own chart for a custom app, and chart versioning — all tested on DomainIndia VPS running k3s.

Why Helm

Without Helm, a typical app needs ~8-15 YAML files (Deployment, Service, Ingress, ConfigMap, Secret, PVC, HPA, ServiceAccount, RBAC...). Values duplicated across files. Updating 5 apps = editing 75 files.

Helm:

  • Templated YAML — DRY
  • Versioned releases — deploy v1.2, rollback to v1.1 instantly
  • Dependencies — "Chart X needs Redis" handled transparently
  • Repositories — 5,000+ pre-built charts on Artifact Hub

Install Helm

bash
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
# version.BuildInfo{Version:"v3.14.0"...}

Works on any machine that can reach your k3s/K8s cluster.

Pattern 1 — Install existing charts

Add a repository:

bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

Search:

bash
helm search repo redis
# NAME                        CHART VERSION  APP VERSION DESCRIPTION
# bitnami/redis              19.5.2         7.2.4       Redis...

Install:

bash
helm install mycache bitnami/redis 
    --namespace cache 
    --create-namespace 
    --set auth.password=SuperSecret123

Check:

bash
helm list --all-namespaces
kubectl get pods -n cache

Uninstall:

bash
helm uninstall mycache -n cache

That's it — Redis running in K8s with 5 commands.

Pattern 2 — Customise via values.yaml

Charts accept configuration. Instead of --set flags, use a values file:

bash
helm show values bitnami/redis > my-values.yaml

Edit my-values.yaml:

yaml
architecture: standalone   # or "replication" for HA
auth:
  password: SuperSecret123
master:
  persistence:
    size: 10Gi
  resources:
    limits:
      memory: 512Mi
      cpu: 500m

Install with values:

bash
helm install mycache bitnami/redis -f my-values.yaml -n cache --create-namespace

Keep my-values.yaml in git. Reproducible deployments.

Pattern 3 — Upgrade in place

bash
# Change something in my-values.yaml
helm upgrade mycache bitnami/redis -f my-values.yaml -n cache

Helm computes the diff, applies only what changed, keeps revision history.

Rollback to previous version:

bash
helm history mycache -n cache
# REVISION  UPDATED     STATUS      CHART
# 1         ...         deployed    redis-19.5.2
# 2         ...         deployed    redis-19.5.3
# 3         ...         deployed    redis-19.5.3  (current)

helm rollback mycache 2 -n cache

Pattern 4 — Write your own chart

bash
helm create myapp

Creates:

myapp/
├── Chart.yaml           # chart metadata
├── values.yaml          # default config
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── serviceaccount.yaml
│   ├── _helpers.tpl     # reusable template functions
│   └── NOTES.txt        # shown after install
└── charts/              # sub-charts (dependencies)

Chart.yaml:

yaml
apiVersion: v2
name: myapp
description: My Node.js API
type: application
version: 0.1.0           # chart version
appVersion: "1.0.0"      # app version

values.yaml:

yaml
replicaCount: 2
image:
  repository: ghcr.io/yourorg/myapp
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: traefik
  host: myapp.yourcompany.com
  tls: true

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 128Mi

env:
  DATABASE_URL: "postgres://..."
  JWT_SECRET: ""    # set via --set or separate secrets

templates/deployment.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: 3000
        env:
        {{- range $key, $value := .Values.env }}
        - name: {{ $key }}
          value: {{ $value | quote }}
        {{- end }}
        resources:
          {{- toYaml .Values.resources | nindent 10 }}

Template language is Go templates with Sprig functions. Common patterns:

yaml
# Conditional
{{- if .Values.ingress.enabled }}
...
{{- end }}

# Iterate
{{- range .Values.env }}
- name: {{ .name }}
  value: {{ .value }}
{{- end }}

# Include helper
{{- include "myapp.fullname" . }}

# Default values
image: {{ .Values.image.repository | default "busybox" }}

Test your chart

bash
# Dry-run — render templates without applying
helm install --dry-run myapp ./myapp

# Diff (requires helm-diff plugin)
helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade myapp ./myapp

# Lint
helm lint ./myapp

Pattern 5 — Secrets management

Never commit secrets in values.yaml.

Option A — External secrets:

Use External Secrets Operator with AWS Secrets Manager / HashiCorp Vault. Your chart references the secret name; actual values live outside git.

Option B — Sealed Secrets:

bash
kubeseal < secret.yaml > sealed-secret.yaml
# sealed-secret.yaml is safe to commit — only cluster can decrypt

Option C — Manually via `--set`:

bash
helm install myapp ./myapp --set env.JWT_SECRET=$JWT_SECRET

OK for CI/CD where secret comes from GitHub Actions env.

Pattern 6 — Dependencies

Chart.yaml:

yaml
dependencies:
  - name: postgresql
    version: "15.5.0"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled

  - name: redis
    version: "19.5.2"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled

Download deps:

bash
helm dependency update

Install with deps:

bash
helm install myapp ./myapp 
    --set postgresql.enabled=true 
    --set postgresql.auth.postgresPassword=secret 
    --set redis.enabled=true

One chart, whole stack (app + DB + cache).

Pattern 7 — Publish your chart

bash
helm package ./myapp
# Creates myapp-0.1.0.tgz

# Host on GitHub Pages
helm repo index ./charts-repo
# or use ChartMuseum, Harbor, Artifact Hub

Others can:

bash
helm repo add yourorg https://yourorg.github.io/charts
helm install myapp yourorg/myapp

Pattern 8 — GitOps with Helm

Argo CD or Flux continuously sync your cluster with Helm charts stored in git.

yaml
# Argo CD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  source:
    repoURL: https://github.com/yourorg/myapp
    path: chart
    targetRevision: main
    helm:
      valueFiles: [values-prod.yaml]
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Push to git → Argo CD detects → applies chart. Always-in-sync cluster.

Common pitfalls

FAQ

Q Helm or Kustomize?

Helm — templating, versioning, dependencies, repositories. Kustomize — overlays, no templating, in-tree with kubectl. Helm wins for 3rd-party apps; Kustomize for your own simple overlays.

Q Do I need Helm for small k3s deployments?

For 1-2 services — probably not. For 5+ services with shared config — yes.

Q Can I use Helm without a Kubernetes cluster?

Yes — helm template renders YAML files you can kubectl-apply manually. But you lose release tracking.

Q Chart registry options?

Artifact Hub (discovery), OCI registries (Docker Hub, GHCR, Harbor), Bitnami, self-hosted ChartMuseum, GitHub Pages.

Q Breaking changes across Helm versions?

Helm 3 (current) removed Tiller — client-only. Helm 2 is dead. All tutorials should target Helm 3+.

Run Helm on your k3s cluster on DomainIndia VPS. Get a VPS

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket