Skip to content
Published on

Helm Architecture Internals: Design Philosophy and Structure of Helm 3

Authors

1. Helm 3 Architecture Overview

1.1 Evolution from Helm 2 to Helm 3

Helm 2 required a server-side component called Tiller within the cluster. While Tiller managed releases and deployed charts, it had serious security issues.

Tiller's Problems:

  • Ran with cluster-admin privileges by default
  • gRPC endpoint exposed without authentication
  • Impossible to separate privileges in multi-tenant environments
  • Caused RBAC bypass

Helm 3 completely removed Tiller. Now the Helm CLI communicates directly with the Kubernetes API server, using the authentication credentials from kubeconfig.

1.2 Client-Only Architecture

+------------------+       +---------------------+
|   Helm CLI       | ----> |  Kubernetes API     |
|  (Client)        |       |  Server             |
+------------------+       +---------------------+
        |                           |
        v                           v
  +-----------+              +-----------+
  | Chart     |              | Release   |
  | Repository|              | Storage   |
  +-----------+              | (Secrets) |
                             +-----------+

Helm 3's architecture is simple:

  1. Helm CLI: Performs chart rendering, API calls, and release management entirely client-side
  2. Kubernetes API Server: Actual execution of resource creation/modification/deletion
  3. Release Storage: Stores release metadata as Secrets or ConfigMaps

1.3 Improved Security Model

# Helm 3 uses kubeconfig context directly
# RBAC is naturally applied
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: helm-deployer
  namespace: production
rules:
  - apiGroups: ['', 'apps', 'batch']
    resources: ['deployments', 'services', 'configmaps', 'secrets']
    verbs: ['get', 'list', 'create', 'update', 'patch', 'delete']

In Helm 3, the user's kubeconfig context is applied directly, enabling fine-grained permission control through Kubernetes RBAC.


2. Release Storage

2.1 Secret-Based Storage

Helm 3 stores release information in Kubernetes Secret resources by default.

# Query release Secrets
kubectl get secrets -l owner=helm -n default

# Secret name format: sh.helm.release.v1.RELEASE_NAME.vREVISION
# Example: sh.helm.release.v1.my-app.v1

Each Secret contains the following data, gzip-compressed and base64-encoded:

  • Chart metadata (Chart.yaml content)
  • Rendered manifests
  • Values (user-provided + defaults merged)
  • Release status (deployed, failed, uninstalled, etc.)
  • Release notes

2.2 ConfigMap-Based Storage

ConfigMap can be used instead of Secrets:

# Use ConfigMap driver
export HELM_DRIVER=configmap
helm install my-app ./my-chart

2.3 Storage Driver Comparison

DriverProsCons
secret (default)etcd encryption support, RBAC access controlSecret resource size limit (1MB)
configmapNo Secret access permissions neededData stored in plaintext
sqlLarge-scale release managementExternal DB required, complex setup
memoryTesting purposesNo persistent storage

3. Chart Structure

3.1 Basic Directory Structure

my-chart/
  Chart.yaml          # Chart metadata (required)
  Chart.lock          # Dependency lock file
  values.yaml         # Default configuration values (required)
  values.schema.json  # Values schema validation
  charts/             # Dependency charts (subcharts)
  crds/               # CRD manifests
  templates/          # Go template files
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl      # Named template helpers
    NOTES.txt         # Release notes template
    tests/            # Test Pod definitions
      test-connection.yaml

3.2 Chart.yaml Details

apiVersion: v2 # Must be v2 for Helm 3
name: my-application
description: A Helm chart for my application
type: application # application or library
version: 1.2.3 # Chart version (SemVer)
appVersion: '2.0.0' # Application version
kubeVersion: '>=1.25.0' # Supported Kubernetes version range
home: https://example.com
sources:
  - https://github.com/example/my-app
maintainers:
  - name: developer
    email: dev@example.com
icon: https://example.com/icon.png
keywords:
  - app
  - web
annotations:
  artifacthub.io/changes: |
    - kind: added
      description: Initial release
dependencies:
  - name: postgresql
    version: '12.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: postgresql.enabled
    tags:
      - database

3.3 CRD Directory

Files in the crds/ directory receive special treatment:

  • Applied only during install; ignored during upgrade or rollback
  • Template rendering is not applied (plain YAML)
  • Not deleted during uninstall (data protection)
# crds/my-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myresources.example.com
spec:
  group: example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
  scope: Namespaced
  names:
    plural: myresources
    singular: myresource
    kind: MyResource

4. Template Engine Basics

4.1 Go Template Basic Syntax

Helm is based on Go's text/template package with additional Sprig library functions.

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: { { include "my-chart.fullname" . } }
  labels: { { - include "my-chart.labels" . | nindent 4 } }
spec:
  replicas: { { .Values.replicaCount } }
  selector:
    matchLabels: { { - include "my-chart.selectorLabels" . | nindent 6 } }
  template:
    metadata:
      labels: { { - include "my-chart.selectorLabels" . | nindent 8 } }
    spec:
      containers:
        - name: { { .Chart.Name } }
          image: '{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}'
          ports:
            - containerPort: { { .Values.service.port } }

4.2 Built-in Objects

Key built-in objects available in Helm templates:

ObjectDescription
.ReleaseRelease info (Name, Namespace, Revision, IsUpgrade, IsInstall)
.ValuesMerged result of values.yaml and user-provided values
.ChartContents of Chart.yaml
.CapabilitiesCluster capability info (API versions, Kubernetes version)
.TemplateCurrent template info (Name, BasePath)
.FilesAccess to chart files
# .Release object usage example
metadata:
  annotations:
    helm.sh/release-name: {{ .Release.Name }}
    helm.sh/release-namespace: {{ .Release.Namespace }}
    helm.sh/revision: {{ .Release.Revision | quote }}

# .Capabilities usage example
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
apiVersion: networking.k8s.io/v1
{{- else }}
apiVersion: networking.k8s.io/v1beta1
{{- end }}

4.3 Sprig Function Examples

# String functions
name: {{ .Values.name | upper }}           # Uppercase
name: {{ .Values.name | lower }}           # Lowercase
name: {{ .Values.name | title }}           # Title case
name: {{ .Values.name | trim }}            # Trim whitespace
name: {{ .Values.name | trunc 63 }}        # Truncate to 63 chars

# Default values
image: {{ .Values.image.tag | default "latest" }}

# Conditional required values
name: {{ required "A name is required" .Values.name }}

# Encoding
data: {{ .Values.secret | b64enc }}

# Date
timestamp: {{ now | date "2006-01-02T15:04:05Z07:00" }}

5. Dependency Management

5.1 Declaring Dependencies in Chart.yaml

# Chart.yaml
dependencies:
  - name: redis
    version: '17.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: redis.enabled
    tags:
      - cache
  - name: postgresql
    version: '12.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    condition: postgresql.enabled
    tags:
      - database
  - name: common
    version: '2.x.x'
    repository: 'https://charts.bitnami.com/bitnami'
    import-values:
      - child: image
        parent: defaultImage

5.2 Chart.lock File

# Download dependencies and generate lock file
helm dependency update ./my-chart

# Build with exact versions from existing Chart.lock
helm dependency build ./my-chart

The Chart.lock file ensures reproducible builds:

# Chart.lock
dependencies:
  - name: redis
    repository: https://charts.bitnami.com/bitnami
    version: 17.15.2
  - name: postgresql
    repository: https://charts.bitnami.com/bitnami
    version: 12.10.0
digest: sha256:abc123def456...
generated: '2026-03-20T10:00:00.000000000Z'

5.3 condition and tags

condition: Enables subcharts only when a specific value is true

# values.yaml
redis:
  enabled: true
postgresql:
  enabled: false # Disable PostgreSQL subchart

tags: Enable/disable subcharts by tag group

# values.yaml
tags:
  cache: true # Enable all dependencies with cache tag
  database: false # Disable all dependencies with database tag

condition takes precedence over tags.

5.4 Subchart Value Overrides

# Override subchart values from parent values.yaml
redis:
  enabled: true
  architecture: standalone
  auth:
    enabled: false
  master:
    persistence:
      size: 1Gi

postgresql:
  enabled: true
  auth:
    postgresPassword: 'my-password'
    database: mydb

5.5 Global Values

# values.yaml
global:
  imageRegistry: myregistry.io
  imagePullSecrets:
    - name: my-pull-secret
  storageClass: fast-ssd

Values under global are accessible from all subcharts.


6. OCI Registry Support

6.1 OCI-Based Chart Management

OCI registry support is GA (Generally Available) since Helm 3.8:

# Login to registry
helm registry login registry.example.com

# Package and push chart as OCI artifact
helm package ./my-chart
helm push my-chart-1.0.0.tgz oci://registry.example.com/charts

# Install chart from OCI registry
helm install my-release oci://registry.example.com/charts/my-chart --version 1.0.0

# Show chart info
helm show chart oci://registry.example.com/charts/my-chart --version 1.0.0

7. Summary

Helm 3's architecture centers on security, simplicity, and extensibility:

  1. Tiller Removal: Operates with CLI only, no server component in the cluster
  2. RBAC Native: kubeconfig-based auth naturally integrates with Kubernetes RBAC
  3. Secret-Based Release Storage: etcd encryption support, release history management
  4. Structured Charts: Clear separation of concerns with Chart.yaml, values.yaml, templates/
  5. OCI Registry Support: Manage charts on the same infrastructure as container images

The next post will analyze the internals of the Helm template engine in greater depth.