Templates & Machine Detection

Lesson 2 · chezmoi · ~12 minutes

In Lesson 1, you managed a file that's identical across machines. But most dotfiles aren't identical — your work laptop has different PATHs, your personal machine loads different tools. Templates let one source file render differently on each machine.

The Mental Model

A template is a source file with placeholders. When chezmoi applies it, it fills in the placeholders with data from the current machine:

How templates render per machine
Template
dot_zshrc.tmpl
+
Machine Data
chezmoi data
Rendered File
~/.zshrc

The same template produces different output on different machines because the data changes — not the template itself. This is the core mechanism that makes chezmoi useful for multi-machine setups.

Go template syntax basics

Chezmoi uses Go's text/template package. Everything between {{ }} delimiters is a template action. Outside those delimiters, text passes through unchanged.

Outputting a value

To insert a variable's value into the rendered file:

# Machine: {{ .chezmoi.hostname }}

On a machine named work-mbp, this renders as:

# Machine: work-mbp

The dot (.) is the root data object. Everything under .chezmoi is machine-detected data that chezmoi populates automatically.

Conditionals

Use {{ if }} / {{ end }} to include blocks conditionally:

{{ if eq .chezmoi.hostname "work-mbp" }}
export CORP_PROXY="http://proxy.corp.internal:8080"
{{ end }}

This line only appears in the rendered file on the machine named work-mbp. On all other machines, it's omitted entirely.

If / else

{{ if eq .chezmoi.os "darwin" }}
export BREW_PREFIX="/opt/homebrew"
{{ else }}
export BREW_PREFIX="/home/linuxbrew/.linuxbrew"
{{ end }}

Whitespace control

Template actions introduce blank lines where the {{ }} blocks are. Use {{- }} and {{ -}} to trim surrounding whitespace:

{{- if eq .chezmoi.os "darwin" }}
export BREW_PREFIX="/opt/homebrew"
{{- end }}

The - trims the newline before or after the action, producing clean output without extra blank lines.

What data is available?

Run chezmoi data to see everything chezmoi knows about the current machine:

chezmoi data

This outputs JSON. The .chezmoi object contains auto-detected machine information. Here are the most useful fields:

Template VariableExample ValueUse Case
.chezmoi.hostnamework-mbpPer-machine config (proxy, paths)
.chezmoi.osdarwin, linuxOS-specific tools and paths
.chezmoi.archamd64, arm64Architecture-specific binaries
.chezmoi.usernameiforsterUser-specific paths
.chezmoi.homeDir/Users/iforsterCross-platform home directory
.chezmoi.osRelease.idubuntu, fedoraLinux distro detection
Explore your data

Run chezmoi data | jq '.chezmoi' to see just the machine-specific fields. This shows you exactly what variables are available for your templates.

The .tmpl suffix convention

Chezmoi distinguishes templates from static files by the .tmpl suffix in the source state:

Source State FileRendered Target
dot_gitconfig~/.gitconfig (copied verbatim)
dot_zshrc.tmpl~/.zshrc (rendered through template engine)

Without .tmpl, chezmoi copies the file as-is. With .tmpl, it processes {{ }} actions before writing the target.

To convert an already-managed file to a template:

chezmoi chattr +template ~/.zshrc

This renames dot_zshrc to dot_zshrc.tmpl in the source state. You can also add a file as a template from the start:

chezmoi add --template ~/.zshrc

Exercise: Conditional PATH by hostname

You'll convert your .zshrc (or .bashrc) to a template that sets machine-specific PATH entries.

Step 1: Check your machine data

chezmoi data | jq '.chezmoi.hostname'

Note the exact hostname string. You'll use this in your conditional.

Step 2: Add your shell config as a template

chezmoi add --template ~/.zshrc

This creates dot_zshrc.tmpl in the source state with your current .zshrc content.

Step 3: Edit the template

chezmoi edit ~/.zshrc

Add a conditional block. For example, if your work machine is named work-mbp and your personal machine is home-mac:

# Shared PATH
export PATH="$HOME/bin:$PATH"

{{- if eq .chezmoi.hostname "work-mbp" }}

# Work-specific tools
export PATH="/opt/corp-tools/bin:$PATH"
export KUBECONFIG="$HOME/.kube/work-config"
{{- else if eq .chezmoi.hostname "home-mac" }}

# Personal tools
export PATH="$HOME/.cargo/bin:$PATH"
export GOPATH="$HOME/go"
{{- end }}

Step 4: Preview the rendered output

chezmoi cat ~/.zshrc

This shows what the template renders to on this machine. Verify the correct branch was chosen.

Step 5: Check the diff and apply

chezmoi diff
chezmoi apply ~/.zshrc

Review the diff first. If it looks right, apply it. Your ~/.zshrc now contains only the machine-appropriate config.

Step 6: Commit

chezmoi cd
git add .
git commit -m "feat: templatize .zshrc with hostname-based PATH"
git push
exit
Template syntax errors

If your template has a syntax error, chezmoi apply will refuse to write the file and show the error. Use chezmoi cat ~/.zshrc to test rendering before applying. You won't corrupt your actual file.

What just happened

CommandWhat it did
chezmoi dataShowed all template variables for this machine
chezmoi add --template <file>Added a file as a template (with .tmpl suffix)
chezmoi chattr +template <file>Converted an existing managed file to a template
chezmoi edit <file>Opened the source state file in your editor
chezmoi cat <file>Rendered the template and printed the result (without applying)

Knowledge check

What does the .tmpl suffix tell chezmoi?
Correct. The .tmpl suffix tells chezmoi to process {{ }} actions through Go's template engine before writing the rendered output to the target path.
The .tmpl suffix means "this file is a template." Chezmoi processes all {{ }} actions through Go's template engine and writes the rendered result to the target. Without .tmpl, the file is copied verbatim.
Which template expression correctly outputs the current OS?
Right. The leading dot (.) accesses the root data object. All chezmoi-provided variables are nested under .chezmoi, so the full path is .chezmoi.os.
In Go templates, the dot (.) represents the root data context. Chezmoi nests its machine data under .chezmoi, so the correct access is {{ .chezmoi.os }}.
What does chezmoi cat ~/.zshrc do?
Exactly. chezmoi cat renders the template as if it were being applied, but only prints the output — it never writes to the target. It's the safe way to preview what apply would produce.
chezmoi cat renders the template with the current machine's data and prints the result to stdout. It doesn't modify any files — it's a safe preview of what apply would write.

Next up

Machine-detected variables cover the basics, but what about data chezmoi can't auto-detect — like whether this machine is "work" vs "personal," or which cloud provider you use? In Lesson 3: Custom Data Files, you'll define your own variables in .chezmoidata.yaml and use them in templates.

Recommended Reading

chezmoi Templating Guide — The full reference for template syntax, functions, and available data. Covers everything from basic conditionals to advanced pipeline functions. ~10 minute read.

Questions? Ask me anything that's unclear. I can explain template rendering in more detail, help debug template syntax errors, or walk through more complex conditional patterns.
← Prev Next →