TL;DR

Chezmoi follows a declarative approach to configuration management. Even if you already use a configuration-management system, you should check out how it compares!

A Custom Configuration Makes You Feel at Home

Your git aliases, shell setup, keyboard shortcuts and even your favorite fonts are tweaked for your own needs and tastes. It’s part of what makes your computer, your computer. All this configuration evolves over years and is slowly refined over time. It may actually be your longuest-living project.

Do you want to start from scratch when you switch computer? Of course not! And you wouldn’t want to break it with adventurous changes either! Versioning it in git backs up the history and eases the management of this important work. Configuration updates are also more convenient to share between machines this way.

Enter Chezmoi

In the git-based configuration management described above, how to account for the small variations (like between your work environment and personal computer)? Git supports conditional inclusion of configuration files, but how about the other tools? What if you want to encrypt your secrets in your config files, to publish your configuration on GitHub?

Chezmoi follows a declarative approach: it has a separate source repository1 to describe a target state, that then gets applied to your home directory. This is possible through templates, that are activated selectively for some files. The rest of this post shows how powerful templating can be.

Account for Variations Between Machines

Let’s start with a simple example. In the git configuration, you put your email, that’s then included in all your commits. Thus, you often want to have a different email in .gitconfig for your work and personal profiles. To tackle this issue, I put the following in my .gitconfig chezmoi template:

[user]
    email = {{ .email }}

and it gets rendered to this:

[user]
    email = me@my.home

on my personal computer and this:

[user]
    email = me@my.work

on my work computer. It serves a purpose similar to conditionally setting your gitconfig, but it is applicable to any program.

Even More Variations with Templates

Now, let’s cover a more powerful use of templates.

You can have multiple files generated from one using “global” templates. For instance, if you use i3 and sway as your windows manager, you likely have slightly different configurations, given that sway’s configuration is mostly compatible with that of i3. To keep everything in one file, while taking advantages of sway-specific features, you can (in your chezmoi repository):

  1. Put your configuration in .chezmoitemplates/sway_i3. You can keep any sway specific section in a templated if statement, like so:
    {{- if eq .f "sway" }}
    # Sway specific stuff here, like output configuration
    {{- end }}
    
  2. In dot_config/i3/config.tmpl (and dot_config/sway/config.tmpl), use something like:
    {{/* Environment */}}
    {{- $env := . -}}
    
    {{/* Flavor */}}
    {{- $f := "i3" -}} # <--- Change this to "sway" in dot_config/sway/config.tmpl
    
    {{- template "sway_i3" dict "f" $f "env" $env -}}
    
    As you may have noticed, chezmoi renames some files in the source state, to encode parameters and for readability. So dot_config/sway/config.tmpl becomes .config/sway/config (.tmpl means the file is executed as a template).
    Also, $env is used to keep the general template context, for instance to check the OS version in our template config file.

.chezmoiignore

Finally, chezmoi supports an ignore file named .chezmoiignore. The concept is similar to the well known .gitignore: patterns listed in this file are ignored by chezmoi. In other words, some files are stored in your chezmoi repository, but do not reflect in your home. This can be used if you put a README.md in your configuration repository, as you don’t want this file in your home directory. Just write:

README.md

in .chezmoiignore and the README.md won’t be copied in your home.

You can do more advanced things with .chezmoiignore as it is a template by default. From the documentation:

{{- if ne .email "firstname.lastname@company.com" }}
# Ignore .company-directory unless configured with a company email
.company-directory # note that the pattern is not dot_company-directory
{{- end }}

Conclusion

Chezmoi has lot more features, like encryption or installation in GitHub Codespaces. It is quite well documented, in particular with a quick start guide.

I’ve switched to it almost two years ago, I don’t regret it at all!

Bonus: Vim Snippet

To update the target state automatically when editing the source file in the chezmoi repo, I use this vim snippet (with the fish shell):

autocmd BufWritePost ~/.local/share/chezmoi/* ! chezmoi apply --source-path %; or for f in (rg -l 'template "%:t"'); chezmoi apply --source-path $f; end

  1. You should store this source repository in a git repository to easily revert changes and share configuration update between various machines. ↩︎