Skip to content

Custom delimiters

Jinja2’s {{ }} collides with Helm, Go text/template, GitHub Actions, Terraform’s ${}-adjacent tooling and plenty of other things you might want to scaffold. deckctl solves this by letting each template pick its own delimiters.

KindStartEnd
Variable{{}}
Block{%%}
Comment{##}
deckctl.yaml
delimiters:
variable_start: "[["
variable_end: "]]"
block_start: "[%"
block_end: "%]"
comment_start: "[#"
comment_end: "#]"

You only need to set what you’re changing — anything omitted falls back to the default. Each pair must be both set or both empty; setting variable_start without variable_end fails validation.

Now a Helm chart renders safely:

# values.yaml.tmpl — rendered by deckctl using [[ ]]
replicaCount: [[ replicas ]]
image:
repository: [[ image_repo ]]
tag: "[[ image_tag ]]"
# templates/deployment.yaml.tmpl — Helm's own {{ }} passes straight through
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "chart.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}

Delimiters only matter for files deckctl actually renders. A file with no template suffix is copied byte-for-byte, {{ }} and all:

helm-chart/
deckctl.yaml
Chart.yaml.tmpl ← rendered (needs variables)
values.yaml.tmpl ← rendered (needs variables)
templates/deployment.yaml ← copied verbatim, Helm syntax untouched
templates/service.yaml ← copied verbatim

This is usually the simpler answer for Helm: only the handful of files that genuinely need substitution get a suffix, and the chart templates stay as-is with no delimiter gymnastics.

Reach for custom delimiters when a single file needs both — deckctl substitution and literal {{ }} in the output.

Any string works, but a few conventions:

DelimitersGood for
[[ ]] / [% %] / [# #]Helm, Go templates, anything using {{ }}
<< >> / <% %>ERB-ish contexts, or when [ is meaningful (JSON/HCL lists)
{{{ }}}Mustache-adjacent contexts — but easy to misread; avoid

Pick something that cannot appear literally in the file type you’re generating.

Copier templates express the same thing through _envops, and deckctl maps it one-to-one:

copier.yml
_envops:
variable_start_string: "[["
variable_end_string: "]]"
block_start_string: "[%"
block_end_string: "%]"
comment_start_string: "[#"
comment_end_string: "#]"

See Copier compatibility.

--dry-run prints the rendered content of every file, which is the fastest way to confirm the delimiters do what you think:

Terminal window
deckctl template apply helm-chart /tmp/out --source local --dry-run