The templating engine
Go templates and the release context.
Helm renders templates with the Go template engine, augmented by the Sprig function library and a set of built-in objects. Inside {{ }} you access data: .Values (user config), .Release (name, namespace, revision, isInstall/isUpgrade), .Chart (Chart.yaml fields), .Capabilities (cluster/API versions), and .Files (bundled files). Combining these produces manifests that adapt to both the chart’s values and the release context.
apiVersion: apps/v1kind: Deploymentmetadata:name: {{ .Release.Name }}-{{ .Chart.Name }}labels:app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}spec:replicas: {{ .Values.replicaCount }}template:spec:containers:- name: appimage: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
Control flow and scope
Templates support conditionals ({{ if }}), loops ({{ range }}), and variables ({{ $x := }}). The with block narrows scope to a sub-object, which cleans up deep paths — but inside it, the dot (.) now refers to that object, so a common bug is losing access to .Values or .Release inside a with/range. The fix is to save the root ($) or reference values through it. Understanding scope is most of Helm-template debugging.
{{- if .Values.ingress.enabled }}apiVersion: networking.k8s.io/v1kind: Ingress{{- with .Values.ingress.annotations }}annotations:{{- toYaml . | nindent 4 }} # . is now .Values.ingress.annotations{{- end }}{{- end }}