πŸ“š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 print quote default Functions

Helm Sprig print function concatenates without spaces, quote wraps in double quotes, default provides fallback values. Template examples and patterns.

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

πŸ’‘ Quick Answer: `print` concatenates values without separators, `quote` wraps strings in double quotes for YAML safety, and `default` provides fallback values when a variable is empty or undefined. These three Sprig functions handle 90% of Helm string formatting needs.

The Problem

Helm templates generate YAML, and YAML is whitespace-sensitive and type-sensitive. Common issues include:

  • Values with special characters breaking YAML parsing
  • Undefined values causing template rendering failures
  • Need to format strings without the spaces that `cat` inserts
flowchart LR
    INPUT["Template Value"] --> PRINT["print<br/>No separators"]
    INPUT --> QUOTE["quote<br/>YAML-safe strings"]
    INPUT --> DEFAULT["default<br/>Fallback values"]
    PRINT --> YAML["Valid YAML Output"]
    QUOTE --> YAML
    DEFAULT --> YAML

The Solution

The `print` Function

`print` concatenates values without spaces (unlike `cat` which adds spaces):

# cat vs print comparison
{{ cat "hello" "world" }}        # β†’ "hello world" (space between)
{{ print "hello" "world" }}      # β†’ "helloworld"  (no space)

# Build resource names
{{ print .Release.Name "-" .Chart.Name }}
# β†’ "myrelease-mychart"

# Construct image references
image: {{ print .Values.image.registry "/" .Values.image.repository ":" .Values.image.tag }}
# β†’ "registry.example.com/myapp:v1.2.3"

`printf` β€” Formatted Output

# printf uses Go fmt verbs
{{ printf "%s-%s" .Release.Name .Chart.Name }}
# β†’ "myrelease-mychart"

# With numbers
{{ printf "port-%d" .Values.service.port }}
# β†’ "port-8080"

# Padding
{{ printf "%-20s %s" .Values.name .Values.version }}
# β†’ "myapp                v1.2.3"

# Common Kubernetes pattern: generate annotation values
annotations:
  app.kubernetes.io/version: {{ printf "%s+build.%s" .Chart.AppVersion .Values.buildId | quote }}

The `quote` Function

`quote` wraps a value in double quotes, escaping special characters:

# Basic quoting
{{ quote .Values.config.message }}
# Input: Hello "World"
# Output: "Hello \"World\""

# CRITICAL for annotations and labels with special chars
metadata:
  annotations:
    description: {{ .Values.description | quote }}
    # Without quote: description: My app: the best β†’ YAML ERROR (colon)
    # With quote:    description: "My app: the best" β†’ Valid YAML

  labels:
    version: {{ .Values.version | quote }}
    # Without quote: version: 1.0 β†’ parsed as float
    # With quote:    version: "1.0" β†’ kept as string

When to Quote

ScenarioWithout `quote`With `quote`Need Quote?
Simple alphanumeric`myapp``β€œmyapp”`Optional
Contains colonYAML error`β€œvalue: here”`Yes
Contains hashTruncated`β€œvalue # here”`Yes
Numeric stringParsed as number`β€œ1.0”`Yes
Boolean-likeParsed as bool`β€œtrue”`Yes
Empty stringMissing`""`Yes
# Real-world: Prometheus annotations
metadata:
  annotations:
    prometheus.io/scrape: {{ .Values.metrics.enabled | quote }}
    prometheus.io/port: {{ .Values.metrics.port | quote }}
    prometheus.io/path: {{ .Values.metrics.path | quote }}

`squote` β€” Single Quotes

# squote uses single quotes instead of double
{{ squote .Values.password }}
# β†’ 'mysecretpassword'

# Useful for shell commands in containers
command:
  - /bin/sh
  - -c
  - echo {{ squote .Values.message }}

The `default` Function

`default` returns a fallback value when the input is empty, nil, zero, or false:

# Basic usage
{{ default "nginx" .Values.image.repository }}
# If .Values.image.repository is set:   β†’ its value
# If .Values.image.repository is empty:  β†’ "nginx"

# Common Kubernetes patterns
image: {{ default "latest" .Values.image.tag }}
replicas: {{ default 1 .Values.replicas }}
namespace: {{ default .Release.Namespace .Values.namespace }}

What Triggers `default`

ValueTriggers default?
`""` (empty string)βœ…
`nil` / undefinedβœ…
`0` (zero)βœ…
`false`βœ…
`[]` (empty list)βœ…
`{}` (empty map)βœ…
`β€œanything”`❌
`1` or more❌
`true`❌
# Chain defaults with other functions
metadata:
  labels:
    app: {{ default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" | quote }}
    version: {{ default .Chart.AppVersion .Values.image.tag | quote }}

# Resource limits with defaults
resources:
  limits:
    cpu: {{ default "500m" .Values.resources.limits.cpu | quote }}
    memory: {{ default "256Mi" .Values.resources.limits.memory | quote }}
  requests:
    cpu: {{ default "200m" .Values.resources.requests.cpu | quote }}
    memory: {{ default "128Mi" .Values.resources.requests.memory | quote }}

Combining All Three

# Full deployment label block
metadata:
  name: {{ printf "%s-%s" .Release.Name (default .Chart.Name .Values.nameOverride) }}
  labels:
    app.kubernetes.io/name: {{ default .Chart.Name .Values.nameOverride | quote }}
    app.kubernetes.io/instance: {{ .Release.Name | quote }}
    app.kubernetes.io/version: {{ default .Chart.AppVersion .Values.image.tag | quote }}
    helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | quote }}

# ConfigMap with safe values
data:
  DATABASE_URL: {{ printf "postgresql://%s:%s@%s:%d/%s"
    (default "postgres" .Values.db.user)
    (default "password" .Values.db.password)
    (default "localhost" .Values.db.host)
    (default 5432 .Values.db.port | int)
    (default "mydb" .Values.db.name) | quote }}

Helper Template Pattern

{{/* templates/_helpers.tpl */}}

{{/* Generate image reference */}}
{{- define "myapp.image" -}}
{{ print (default "docker.io" .Values.image.registry) "/" .Values.image.repository ":" (default .Chart.AppVersion .Values.image.tag) }}
{{- end }}

{{/* Generate fullname with defaults */}}
{{- define "myapp.fullname" -}}
{{ printf "%s-%s" .Release.Name (default .Chart.Name .Values.fullnameOverride) | trunc 63 | trimSuffix "-" }}
{{- end }}

Common Issues

IssueCauseFix
YAML parse error on colonsUnquoted value with `:`Pipe through `quote`
`1.0` becomes floatYAML type inferenceUse `quote` for version strings
`β€œtrue”` becomes booleanYAML type inferenceUse `quote` to preserve string
Default not triggeringValue is `0` or `false` intentionallyUse `ternary` or `if` instead
Spaces in print outputUsing `cat` instead of `print`Switch to `print` for no-space concat
Nested quotes brokenDouble-escapingUse `squote` for outer, `quote` for inner

Best Practices

  • Always `quote` annotations and labels β€” prevents YAML parsing surprises
  • Use `default` for every optional value β€” makes charts self-documenting
  • Prefer `printf` over `print` β€” format strings are more readable
  • Quote numeric strings β€” version numbers, port strings, boolean-like values
  • Chain functions with pipes β€” `{{ .Values.name | default β€œapp” | quote }}`
  • Test with `helm template` β€” always verify output before deploying

Key Takeaways

  • `print` concatenates without spaces (vs `cat` which adds spaces)
  • `printf` uses Go format verbs (`%s`, `%d`) for structured string formatting
  • `quote` wraps in double quotes and escapes β€” essential for YAML safety
  • `default` provides fallback for empty/nil/zero/false values
  • Combine all three for robust, self-documenting Helm templates
#helm #sprig #templating #functions #string-manipulation
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