Chezmoi dotfile management expertise
Comprehensive guidance on managing dotfiles with chezmoi, including templating, cross-platform configuration, and secret management.
dot_ → . (hidden files)private_ → Sets 0600 permissionsreadonly_ → Sets 0444 permissionsexecutable_ → Sets executable bitrun_ → Scripts that run (once, onchange, always)modify_ → Modifies existing filescreate_ → Creates files if they don't existsymlink_ → Creates symbolic links.tmpl → Go template files.literal → Treat as literal (no templating)dot_zshrc.tmpl → ~/.zshrc (templated)
private_dot_ssh/config.tmpl → ~/.ssh/config (0600, templated)
run_onchange_install-packages.sh.tmpl → Runs when content changes
{{ .chezmoi.hostname }} // Machine hostname
{{ .chezmoi.os }} // Operating system (darwin, linux, windows)
{{ .chezmoi.arch }} // Architecture (amd64, arm64)
{{ .chezmoi.username }} // Current username
{{ .chezmoi.homeDir }} // Home directory path
{{ .chezmoi.sourceDir }} // Chezmoi source directory
{{ if eq .chezmoi.os "darwin" }}
# macOS-specific configuration
{{ else if eq .chezmoi.os "linux" }}
# Linux-specific configuration
{{ end }}
{{ if .is_work_machine }}
# Work-specific settings
{{ end }}
{{- $email := promptStringOnce . "email" "Email address" -}}
export EMAIL="{{ $email }}"
{{- if hasKey . "gpg_key" }}
export GPG_KEY="{{ .gpg_key }}"
{{- end }}
[data]
email = "user@example.com"
is_work_machine = false
[data.github]
username = "myusername"
[diff]
exclude = ["scripts"]
[merge]
command = "nvim"
args = ["-d", "{{ .Destination }}", "{{ .Source }}"]
[data]
weather = """{{ output "curl" "-s" "wttr.in/?format=%c+%t" }}"""
hostname = """{{ output "hostname" "-s" }}"""
# Bitwarden
export GITHUB_TOKEN="{{ (bitwarden "item" "github-token").login.password }}"
# 1Password
export API_KEY="{{ onepasswordRead "op://Personal/API Key/password" }}"
# Encrypted files
{{ includeTemplate "private_dot_ssh/id_rsa.tmpl" . | decrypt }}
# Encrypt a file
chezmoi add --encrypt ~/.ssh/id_rsa
# Decrypt for editing
chezmoi edit ~/.ssh/id_rsa
# Set encryption method in .chezmoi.toml
encryption = "age" # or "gpg"
run_once_ → Runs once onlyrun_onchange_ → Runs when script content changesrun_always_ → Runs every timerun_before_ → Before applying dotfilesrun_after_ → After applying dotfilesrun_onchange_before_10-install.sh# run_onchange_before_install-packages.sh.tmpl
#!/bin/bash
{{ if eq .chezmoi.os "darwin" -}}
brew bundle --file=- <<EOF
{{ range .packages.darwin -}}
{{ . }}
{{ end -}}
EOF
{{ else if eq .chezmoi.os "linux" -}}
sudo apt-get update
sudo apt-get install -y {{ range .packages.linux }}{{ . }} {{ end }}
{{ end -}}
.chezmoiremove # Files to remove
.chezmoiignore # Files to ignore
.chezmoitemplates # Shared templates
.chezmoiexternal # External files to download
[".oh-my-zsh"]
type = "archive"
url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz"
exact = true
stripComponents = 1
[".config/nvim/autoload/plug.vim"]
type = "file"
url = "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"
~/dotfiles/
├── dot_config/
│ ├── nvim/
│ └── git/
├── private_dot_ssh/
├── dot_zshrc.tmpl
├── .chezmoi.toml.tmpl
├── .chezmoiignore
└── run_onchange_install-packages.sh.tmpl
run_onchange_ for package installationmodify_ for partial file changes{{ $configDir := .chezmoi.homeDir }}
{{ if eq .chezmoi.os "darwin" }}
{{ $configDir = printf "%s/Library/Application Support" .chezmoi.homeDir }}
{{ else }}
{{ $configDir = printf "%s/.config" .chezmoi.homeDir }}
{{ end }}
# .chezmoi.toml.tmpl
{{ $hostname := .chezmoi.hostname }}
{{ if eq $hostname "work-laptop" }}
[data]
is_work_machine = true
proxy = "http://proxy.company.com:8080"
{{ else }}
[data]
is_work_machine = false
{{ end }}
# dot_gitconfig.tmpl
[user]
name = {{ .name | quote }}
email = {{ .email | quote }}
{{ if .is_work_machine }}
signingkey = {{ .work_gpg_key | quote }}
{{ end }}
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
chezmoi init # Initialize chezmoi
chezmoi add ~/.zshrc # Add a file
chezmoi edit ~/.zshrc # Edit the source file
chezmoi diff # Show what would change
chezmoi apply # Apply changes
chezmoi update # Pull and apply latest
# Working with templates
chezmoi execute-template < file.tmpl # Test templates
chezmoi data # Show template data
# Managing secrets
chezmoi add --encrypt file # Add encrypted
chezmoi decrypt file # Decrypt file
{{- and -}} to control whitespaceprivate_ prefix for sensitive fileschezmoi doctor # Check configuration
chezmoi verify # Verify dotfiles
chezmoi apply --dry-run -v # Verbose dry run
# String functions
{{ .variable | quote }} # Quote a string
{{ .variable | upper }} # Uppercase
{{ .variable | lower }} # Lowercase
{{ printf "%s/%s" .dir .file }} # Format string
# Conditionals
{{ if eq .var "value" }}...{{ end }}
{{ if ne .var "value" }}...{{ end }}
{{ if and .a .b }}...{{ end }}
{{ if or .a .b }}...{{ end }}
# Includes
{{ include "template-name" . }}
{{ includeTemplate "file.tmpl" . }}