Install
# One-liner that works on most platforms
sh -c "$(curl -fsLS get.chezmoi.io)"
# Or via package manager
brew install chezmoi
sudo apt install chezmoi
sudo pacman -S chezmoi
# Or with mise (see /tutorials/mise-polyglot-runtime-versions.html)
mise use -g chezmoi@latest
Initialize on the first machine
# Initialize a new chezmoi source directory in ~/.local/share/chezmoi
chezmoi init
# Or clone an existing one from a Git remote
chezmoi init git@github.com:amir/dotfiles.git
The "source directory" (~/.local/share/chezmoi) holds the templated versions of your dotfiles. chezmoi apply renders templates and writes the result to ~/.
Add your first dotfile
# Take an existing ~/.bashrc and add it to chezmoi
chezmoi add ~/.bashrc
# Now ~/.local/share/chezmoi/dot_bashrc exists (note the dot_ prefix)
chezmoi cd # opens shell in the source dir
ls
# dot_bashrc
# Edit through chezmoi (so changes are tracked in the source)
chezmoi edit ~/.bashrc
The naming convention is precise: dot_ → ., private_ → permission 600, executable_ → chmod +x. dot_ssh/private_config in the source becomes ~/.ssh/config mode 600.
Apply
chezmoi diff # what would change?
chezmoi apply -v # apply, showing each file
chezmoi managed # list every file chezmoi manages
Push to Git, clone on a new machine
chezmoi cd
git init
git add .
git commit -m "Initial dotfiles"
git remote add origin git@github.com:amir/dotfiles.git
git push -u origin main
# On a new machine
chezmoi init --apply git@github.com:amir/dotfiles.git
# Clones the source, applies to ~/. Two commands; full dotfile set installed.
Templates: same source, per-machine output
The killer feature. Source files ending in .tmpl are rendered as Go templates:
# dot_gitconfig.tmpl
[user]
name = Amir Eslampanah
email = {{ if eq .chezmoi.hostname "work-laptop" }}amir@work.example.com{{ else }}amir@personal.example.com{{ end }}
[core]
editor = {{ if lookPath "hx" }}hx{{ else }}vim{{ end }}
[init]
defaultBranch = main
{{ if eq .chezmoi.os "darwin" }}
[credential]
helper = osxkeychain
{{ end }}
chezmoi exposes a rich .chezmoi variable: hostname, os, osRelease, arch, username, homedir, plus your own custom .config.yaml.tmpl values. Combine these to produce one set of dotfiles that adapts per-machine.
Secrets
The thing that makes dotfile-management-with-Git fundamentally tricky: some files contain secrets (API keys, SSH host configs with hostnames you don't want public). chezmoi's secret integrations:
# 1Password
{{ (onepassword "AWS Production Key").fields.password }}
# Bitwarden / Vaultwarden (see /tutorials/vaultwarden-self-hosted-bitwarden.html)
{{ (bitwarden "item" "github-token").login.password }}
# HashiCorp Vault (see /tutorials/hashicorp-vault-secrets.html)
{{ (vault "secret/data/aws").data.data.access_key }}
# KeePassXC
{{ (keepassxc "GitHub Token").Password }}
# Generic age-encrypted file in the source
{{ (decrypt (include "private_secrets.age")) }}
Rendered at chezmoi apply time on each machine; the unencrypted value never lives in Git. For machines that won't have access to the secret store (a server bootstrap, a CI job), chezmoi can degrade gracefully — render a placeholder instead.
Scripts: bootstrap a new machine
# run_once_install-packages.sh.tmpl
#!/bin/bash
set -e
{{ if eq .chezmoi.os "linux" -}}
{{ if eq .chezmoi.osRelease.id "debian" "ubuntu" -}}
sudo apt install -y ripgrep fd-find bat eza zoxide fzf
{{ else if eq .chezmoi.osRelease.id "arch" -}}
sudo pacman -S --needed --noconfirm ripgrep fd bat eza zoxide fzf
{{ end -}}
{{ else if eq .chezmoi.os "darwin" -}}
brew install ripgrep fd bat eza zoxide fzf
{{ end -}}
# Install rust + cargo tools
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
cargo install starship
Files named run_once_* execute once per machine (chezmoi tracks the hash); run_onchange_* re-run when the script content changes; run_before_* / run_after_* control ordering relative to file application. Bootstrap a fresh machine to fully-configured in one command:
chezmoi init --apply git@github.com:amir/dotfiles.git
# Installs packages, drops dotfiles, configures shell, installs starship, etc.
Per-host overrides without templates
Templates handle most variation, but for files that are wholly machine-specific:
# Same source file but only applied on specific hosts
dot_ssh/config_hostname.work-laptop # only on work-laptop
dot_ssh/config_os.darwin # only on macOS
private_dot_aws/config_user.amir # only when running as amir
The everyday workflow
# Pull updates from Git, apply
chezmoi update # = git pull + apply
# Modify a dotfile through chezmoi
chezmoi edit ~/.zshrc
# Apply with confirmation
chezmoi diff
chezmoi apply
# Push your changes back
chezmoi cd
git add .; git commit -m "..."; git push
chezmoi vs alternatives
- GNU Stow — symlinks files from a Git repo into
~/. No templating; no secrets. Works for simple cases. - yadm — "yet another dotfile manager"; Git wrapper that treats
$HOMEas a Git repo. Templating via Jinja2; secrets via encryption. Smaller scope than chezmoi. - Nix Home Manager — declarative Nix configuration of
~/. Vastly more powerful (manages packages + dotfiles); requires committing to Nix. - Dotbot, rcm, GitHub-as-source-symlinks — various older alternatives; chezmoi has overtaken most of them in active community.
For "I want a single Git repo that fully describes my home directory, works across Linux + macOS + work-vs-personal machines, and handles secrets without bleeding them into Git history," chezmoi is the right size in 2026.