chezmoi
chezmoi stores dotfiles in a git repository, applys them on each machine, and supports template-based differences per system. There are plenty of tutorials online, but what I really want to discuss here is the boundary: what should be managed by it, and what shouldn't.
My criterion is: only include files that I write myself and need to keep consistent across machines. Currently, this includes cross-platform items: WezTerm's ~/.wezterm.lua, Neovim configuration on Windows, Windows PowerShell profile, and AutoHotKey. I edit these on both machines, so maintaining consistency is valuable.
Conversely, I haven't included the bulk of my Linux desktop setup — Hyprland, Quickshell, waybar — for two reasons. First, the volume is large (Quickshell alone is 70,000 lines). Second, since it updates with the upstream rice, I prefer to "follow upstream + make a few local changes" rather than pulling someone else's tens of thousands of lines of QML into my dotfiles repository as a burden. Also, anything containing secrets is excluded — those should be handled via environment variables or a separate .gitignore, and never committed to any repository history.
The remaining daily workflow is simple:
&& &&
On a new machine, a single chezmoi init --apply <repo> pulls and sets everything up.
Two mechanisms are worth knowing; they are sufficient:
Filename prefixes denote attributes — chezmoi does not store metadata; instead, it encodes attributes into the source filename: dot_foo → ~/.foo, private_ → permission 600, executable_ → add executable bit, readonly_, encrypted_ (decrypts on apply using age/gpg), and scripts starting with run_ execute during apply (run_once_ runs only once, suitable for package installation/initialization). Just look at the filename to know what it will become.
Templates handle machine-specific differences — *.tmpl files use Go templates, which can read built-in variables like .chezmoi.os and .chezmoi.hostname, as well as values you define yourself in .chezmoidata or the config:
{{ if eq .chezmoi.os "windows" }}
shell = "pwsh"
{{ else }}
shell = "zsh"
{{ end }}
The same source file produces different results when applied on Windows versus Linux — this is exactly why my cross-platform configurations (WezTerm, PowerShell, Neovim on Windows) can share a single source. chezmoi diff always lets me see clearly what the template will render before applying.
It’s sufficient and not complex — the complexity never lies in the tool, but in the discipline of "resisting the urge to throw everything into it."