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 $HOME as 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.