Template
Render Go templates with structured data. The template executor processes a script body as a Go text/template and writes the result to stdout or a file. It is useful for generating configuration files, reports, or any text output from structured data without spawning a shell.
How It Works
- The
scriptfield is parsed as a Go template. - Data from
config.datais passed to the template as the root context (.). - The rendered output is written to stdout (capturable with
output:), or to a file ifconfig.outputis set.
Dagu skips all variable expansion (${VAR}, command substitution, etc.) on the script body. The template engine is the sole evaluator — ${VAR} literals in your template are preserved as-is. Data values in config.data are expanded by Dagu before being passed to the template, so ${VAR} in data values works normally.
Config Fields
| Field | Type | Required | Description |
|---|---|---|---|
data | object | No | Key-value pairs accessible as {{ .key }} in the template. Values can be strings, numbers, lists, or nested objects. |
output | string | No | File path to write the rendered output to. If empty, output goes to stdout. Relative paths resolve against the step's working_dir. |
Basic Example
steps:
- name: render
type: template
config:
data:
greeting: hello
script: |
{{ .greeting }}, world!
output: RESULTRESULT captures hello, world!.
Writing to a File
steps:
- name: render
type: template
config:
output: /tmp/report.md
data:
title: Monthly Report
script: |
# {{ .title }}
Generated by Dagu.When config.output is set, the rendered content is written atomically to that path. Parent directories are created automatically. Stdout remains empty.
Relative paths resolve against working_dir:
steps:
- name: render
type: template
working_dir: /opt/reports
config:
output: subdir/output.txt
data:
msg: hello
script: "{{ .msg }}"This writes to /opt/reports/subdir/output.txt.
Using Data from Prior Steps
Data values are expanded by Dagu before the template runs, so captured output variables work:
type: graph
steps:
- id: producer
command: 'echo -n "Alice"'
output: NAME
- id: render
depends:
- producer
type: template
config:
data:
name: ${NAME}
script: "Hello, {{ .name }}!"
output: RESULTRESULT captures Hello, Alice!.
Template Functions
The template executor provides functions from slim-sprig (the hermetic subset — no env, network, or random access) plus Dagu-specific overrides. All functions use pipeline-compatible argument order (the pipeline value is the last argument).
Dagu-specific functions
These override or extend slim-sprig with pipeline-friendly argument order:
| Function | Signature | Description |
|---|---|---|
split | split sep s | Split string s by separator sep. Returns []string. |
join | join sep list | Join a list with separator sep. Accepts []string, []any, or any slice. |
count | count v | Length of a slice, map, array, or string. |
add | add b a | Integer addition: a + b. Pipeline: {{ 5 | add 3 }} → 8. |
empty | empty v | Returns true if the value is nil, empty string, or empty collection. |
upper | upper s | Uppercase string. |
lower | lower s | Lowercase string. |
trim | trim s | Trim whitespace from both ends. |
default | default def val | Returns def if val is empty/nil/zero; otherwise returns val. |
Selected slim-sprig functions
These come directly from slim-sprig and work as documented in the slim-sprig docs:
| Function | Example |
|---|---|
replace | {{ "hello world" | replace "world" "dagu" }} → hello dagu |
contains | {{ contains "ell" "hello" }} → true |
hasPrefix / hasSuffix | {{ hasPrefix "hel" "hello" }} → true |
list | {{ list "a" "b" "c" }} → creates a list |
uniq | {{ .items | uniq }} → deduplicate |
sortAlpha | {{ .items | sortAlpha }} → sort strings |
dict | {{ $d := dict "key" "value" }} → create a map |
get | {{ get .map "key" }} → safe map access (returns "" if missing) |
dig | {{ dig "a" "b" "fallback" .data }} → nested map access |
has | {{ list "a" "b" | has "a" }} → true |
toJson / toPrettyJson | Serialize to JSON |
regexMatch / regexFind | Regex operations |
toString | {{ 42 | toString }} → "42" |
substr | {{ substr 0 5 "hello world" }} → hello |
Blocked functions
These functions are removed for safety — they will cause a template parse error if used:
- Environment access:
env,expandenv - Network I/O:
getHostByName - Non-deterministic time:
now,date,dateInZone,ago,duration,unixEpoch, etc. - Crypto key generation:
genPrivateKey,derivePassword,genCA,genSelfSignedCert,genSignedCert,buildCustomCert - Random generation:
randBytes,randString,randNumeric,randAlphaNum,randAlpha,randAscii,randInt,uuidv4
Missing Key Behavior
Templates use missingkey=error. Referencing a key not present in data causes the step to fail:
steps:
- name: render
type: template
config:
data:
name: test
script: "{{ .undefined_key }}" # Fails with execution errorUse default to handle optional keys safely:
script: '{{ .name | default "Anonymous" }}'Or use get for safe map access:
script: '{{ get .app "owner" | default "unknown" }}'Complex Example
steps:
- name: render-config
type: template
script: |
app={{ .app.name | lower | replace " " "-" }}
owner={{ get .app "owner" | default "unknown" }}
domains={{ get .app "domains" | default (list "localhost") | uniq | sortAlpha | join "," }}
config:
data:
app:
name: My Service
domains:
- api.example.com
- api.example.com
- app.example.com
output: RESULTOutput:
app=my-service
owner=unknown
domains=api.example.com,app.example.comDollar Sign Preservation
Because Dagu skips expansion on the script body, shell-style variables like ${BAR} and backtick expressions pass through unchanged:
steps:
- name: render
type: template
config:
data:
name: test
script: |
export FOO=${BAR}
echo "{{ .name }}"
value=`command`
output: RESULTThe output contains literal ${BAR} and `command`.
Pipeline Chaining
Functions compose naturally in pipelines:
script: '{{ "a,b,c" | split "," | join ";" }}'
# Result: a;b;cscript: '{{ .csv | split "," | count }}'
# With csv: "x,y,z" → 3script: '{{ .domains | uniq | sortAlpha | join "," }}'
# slim-sprig list functions return []any; join accepts both []string and []any