- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. Helm 3 Architecture Overview
- 2. Release Storage
- 3. Chart Structure
- 4. Template Engine Basics
- 5. Dependency Management
- 6. OCI Registry Support
- 7. Summary
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:
- Helm CLI: Performs chart rendering, API calls, and release management entirely client-side
- Kubernetes API Server: Actual execution of resource creation/modification/deletion
- 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
| Driver | Pros | Cons |
|---|---|---|
| secret (default) | etcd encryption support, RBAC access control | Secret resource size limit (1MB) |
| configmap | No Secret access permissions needed | Data stored in plaintext |
| sql | Large-scale release management | External DB required, complex setup |
| memory | Testing purposes | No 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:
| Object | Description |
|---|---|
.Release | Release info (Name, Namespace, Revision, IsUpgrade, IsInstall) |
.Values | Merged result of values.yaml and user-provided values |
.Chart | Contents of Chart.yaml |
.Capabilities | Cluster capability info (API versions, Kubernetes version) |
.Template | Current template info (Name, BasePath) |
.Files | Access 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:
- Tiller Removal: Operates with CLI only, no server component in the cluster
- RBAC Native: kubeconfig-based auth naturally integrates with Kubernetes RBAC
- Secret-Based Release Storage: etcd encryption support, release history management
- Structured Charts: Clear separation of concerns with Chart.yaml, values.yaml, templates/
- 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.