πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Helm beginner ⏱ 10 minutes K8s 1.28+

Helm Sprig add1 trim merge Functions

Helm Sprig add1 function increments integers in templates. Plus trim for whitespace removal and merge for combining dictionaries in Helm charts.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: `add1` increments an integer by 1 (useful for port offsets and index shifting), `trim` removes leading/trailing whitespace from strings, and `merge` combines multiple dictionaries with first-wins precedence. These utility functions solve common Helm templating edge cases.

The Problem

Helm templates often need small transformations β€” incrementing a port number, cleaning up whitespace from multi-line values, or merging default configurations with user overrides. Without these utility functions, you end up with verbose workarounds and fragile templates.

The Solution

The `add1` Function

`add1` increments an integer by 1:

# Basic increment
{{ add1 8080 }}    # β†’ 8081
{{ add1 0 }}       # β†’ 1

# Port offset patterns
ports:
  - name: http
    containerPort: {{ .Values.port }}
  - name: metrics
    containerPort: {{ add1 .Values.port }}
  - name: admin
    containerPort: {{ add .Values.port 2 }}

# Loop index (range is 0-based, but some configs need 1-based)
{{- range $i, $worker := .Values.workers }}
  worker-{{ add1 $i }}:   # worker-1, worker-2, worker-3...
    replicas: {{ $worker.replicas }}
{{- end }}

# Retry count (retries = attempts - 1)
backoffLimit: {{ add1 .Values.maxRetries }}
{{ add 5 3 }}       # β†’ 8
{{ sub 10 3 }}      # β†’ 7
{{ mul 4 3 }}       # β†’ 12
{{ div 10 3 }}      # β†’ 3 (integer division)
{{ mod 10 3 }}      # β†’ 1
{{ max 5 10 3 }}    # β†’ 10
{{ min 5 10 3 }}    # β†’ 3

# Calculate resource limits
memory: {{ mul .Values.workersPerNode 256 }}Mi
# 4 workers Γ— 256Mi = 1024Mi

The `trim` Function

`trim` removes leading and trailing whitespace:

# Basic trim
{{ trim "  hello  " }}           # β†’ "hello"
{{ trim "\n  hello\n  " }}       # β†’ "hello"

# Clean up values that might have whitespace
image: {{ trim .Values.image.repository }}:{{ trim .Values.image.tag }}

# Trim variants
{{ trimPrefix "v" .Values.version }}   # "v1.2.3" β†’ "1.2.3"
{{ trimSuffix "/" .Values.baseUrl }}    # "https://api.example.com/" β†’ "https://api.example.com"
{{ trimAll "/" "/path/to/thing/" }}     # β†’ "path/to/thing"

# Common pattern: clean name for label (max 63 chars)
metadata:
  labels:
    app: {{ .Values.name | trim | trunc 63 | trimSuffix "-" | quote }}

`trim` Expects String Error

# ERROR: trim expects a string, got int
{{ trim .Values.port }}  # fails if port is 8080 (int)

# FIX: convert first
{{ .Values.port | toString | trim }}

# Or use printf
{{ printf "%d" .Values.port | trim }}

The `merge` Function

`merge` combines dictionaries. First dictionary wins on conflicts:

# merge syntax: merge $dest $source1 $source2 ...
# $dest values take precedence

# Default annotations + user overrides
{{- $defaultAnnotations := dict
  "app.kubernetes.io/managed-by" "Helm"
  "prometheus.io/scrape" "true"
  "prometheus.io/port" "9090"
-}}
{{- $annotations := merge (.Values.annotations | default dict) $defaultAnnotations -}}
metadata:
  annotations:
    {{- toYaml $annotations | nindent 4 }}

# Default resource limits + overrides
{{- $defaultResources := dict
  "limits" (dict "cpu" "500m" "memory" "256Mi")
  "requests" (dict "cpu" "200m" "memory" "128Mi")
-}}
{{- $resources := merge (.Values.resources | default dict) $defaultResources -}}
resources:
  {{- toYaml $resources | nindent 2 }}

`merge` vs `mergeOverwrite`

# merge: first dict wins (user values preserved)
{{ merge (dict "a" "user") (dict "a" "default" "b" "default") }}
# β†’ {"a": "user", "b": "default"}

# mergeOverwrite: last dict wins
{{ mergeOverwrite (dict "a" "user") (dict "a" "override" "b" "new") }}
# β†’ {"a": "override", "b": "new"}

Practical Example: Complete Deployment

{{- $defaults := dict
  "replicas" 1
  "port" 8080
  "image" (dict "tag" .Chart.AppVersion "pullPolicy" "IfNotPresent")
-}}
{{- $config := merge .Values $defaults -}}

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ printf "%s-%s" .Release.Name .Chart.Name | trim | trunc 63 | trimSuffix "-" }}
  labels:
    app: {{ .Chart.Name | quote }}
    version: {{ default .Chart.AppVersion .Values.image.tag | quote }}
spec:
  replicas: {{ $config.replicas }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          ports:
            - name: http
              containerPort: {{ $config.port }}
            - name: metrics
              containerPort: {{ add1 $config.port }}

Common Issues

IssueCauseFix
`add1` on stringPort passed as stringUse `int` first: `{{ .Values.port | int | add1 }}`
`trim` on non-stringInteger or bool valueConvert first: `{{ toString .Values.x | trim }}`
`merge` not deepNested dicts not mergedUse `mergeOverwrite` for deep override behavior
Whitespace in outputTemplate indentationUse `{{-` and `-}}` trim markers

Best Practices

  • Use `add1` for port offsets β€” cleaner than `{{ add .port 1 }}`
  • Always `trim` external inputs β€” values from files or external sources may have whitespace
  • Use `merge` for defaults β€” keeps user values, fills gaps with defaults
  • `trimSuffix ”-”` after `trunc` β€” prevents labels ending in `-`
  • Chain functions β€” `{{ .Values.name | trim | lower | trunc 63 | trimSuffix ”-” }}`

Key Takeaways

  • `add1` increments integers β€” common for port offsets and 1-based indexing
  • `trim`, `trimPrefix`, `trimSuffix` clean strings β€” essential for YAML-safe output
  • `merge` combines dicts with first-wins precedence β€” perfect for default values
  • `mergeOverwrite` uses last-wins β€” for when overrides should take priority
  • Always check types β€” `trim` expects strings, `add1` expects integers
#helm #sprig #templating #functions #math
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens