Machine specific config#

This repo uses one shared dotfiles source while still allowing each machine to load the right variant of a config file. The selection is deterministic and based on machine identity signals and environment values available to chezmoi during rendering.

Generic selection mechanism#

The machine identity selectors are built from three reusable templates:

  • os-config from os-config.tmpl

  • user-config from user-config.tmpl

  • os-user-config from os-user-config.tmpl

os-config.tmpl#
1{{- /* vim: set ft=gotmpl: */ -}}
2{{- if eq .chezmoi.os "linux" -}}
3{{ includeTemplate "linux-config.tmpl" . }}
4{{- else if eq .chezmoi.os "windows" -}}
5windows
6{{- else -}}
7uncharted
8{{- end -}}
user-config.tmpl#
1{{- /* vim: set ft=gotmpl: */ -}}
2{{- last (splitList "\\" .chezmoi.username) -}}
os-user-config.tmpl#
1{{- /* vim: set ft=gotmpl: */ -}}
2{{- $os := includeTemplate "os-config.tmpl" . -}}
3{{- $user := includeTemplate "user-config.tmpl" . -}}
4{{- print $os "-" $user -}}

Symlink templates use these selectors to resolve a candidate target name. If that candidate exists in source state, chezmoi creates a symlink to it.

The candidate source can be either a regular file (<candidate>) or a template (<candidate>.tmpl). This lets us make os-specific or user-specific source files templates when we need dynamic content, while still linking to the same runtime target name.

No-op fallback for fault tolerance#

Each symlink selector checks whether the computed candidate exists in source state before emitting it. If neither source form exists, the template falls back to a no-op target file.

This keeps the generated symlink valid even when a machine-specific variant is not present.

Generic robust selector pattern#
{{- $source_dir := joinPath .chezmoi.sourceDir "<config-root>" -}}
{{- $candidate := print "<prefix>-" (includeTemplate "os-config.tmpl" .) "<suffix>" -}}
{{- $candidate_tmpl := print $candidate ".tmpl" -}}
{{- if or (stat (joinPath $source_dir $candidate)) (stat (joinPath $source_dir $candidate_tmpl)) -}}
{{ $candidate }}
{{- else -}}
no-op.<ext>
{{- end -}}

Selection flow#

  1. Chezmoi evaluates a symlink template.

  2. The template renders a selector value (os, user, or os-user).

  3. The template builds a candidate target name from that selector.

  4. If <candidate> or <candidate>.tmpl exists in source state, <candidate> is used.

  5. If the candidate is missing, the no-op target is used.

  6. Chezmoi creates the symlink to the selected target.

Where this is used#