Manage Dotfiles with chezmoi
Track, template, and encrypt your dotfiles with chezmoi — then restore any machine to your exact config in one command.
Before you start
- ▸Git installed and configured with your name and email
- ▸A remote Git repository (GitHub, Gitea, etc.) if you want to sync across machines
- ▸age installed for encrypted secrets (optional but recommended)
- ▸Basic familiarity with editing dotfiles and working in a terminal
Dotfiles are the soul of a configured system — your shell aliases, editor settings, Git config, and more. Keeping them in sync across multiple machines without accidentally committing secrets is the real challenge. chezmoi solves this by treating your home directory as a managed state, supporting templates, and integrating with secret managers so sensitive values never land in your repo.
How chezmoi Works
chezmoi maintains a source directory (default ~/.local/share/chezmoi) that mirrors your home directory. Files there are prefixed and transformed before being written to their destination. When you run chezmoi apply, it reconciles the source state against your actual home directory, only touching what has changed.
Key concepts you'll use constantly:
- Source state — files under
~/.local/share/chezmoi, possibly templated - Target state — the rendered result chezmoi wants your home directory to contain
- Actual state — what's really on disk in your home directory
Installation
Install chezmoi from your distro's package manager. The upstream also ships a one-liner installer if your distro lags behind.
Debian / Ubuntu
sudo apt install chezmoi
Fedora / RHEL / Rocky
sudo dnf install chezmoi
Arch
sudo pacman -S chezmoi
Universal installer (any distro, always current)
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b ~/.local/bin
Make sure ~/.local/bin is in your PATH if you use the universal installer.
Initialising Your Dotfile Repository
If you are starting fresh, run chezmoi init with no arguments. This creates the source directory and an empty Git repository inside it.
chezmoi init
If you already have a dotfiles repo on GitHub (or another host), pass the repository URL and chezmoi clones it into the source directory automatically:
chezmoi init https://github.com/youruser/dotfiles.git
Confirm the source directory path:
chezmoi source-path
Output will be something like /home/alice/.local/share/chezmoi.
Adding and Managing Files
Tell chezmoi to track a file with chezmoi add. It copies the file into the source directory with the correct prefix.
chezmoi add ~/.bashrc ~/.gitconfig ~/.config/nvim/init.lua
Edit the source copy directly (preferred):
chezmoi edit ~/.bashrc
Preview what apply would change before committing:
chezmoi diff
Apply the source state to your home directory:
chezmoi apply
After applying, commit the source directory to Git:
chezmoi cd
git add -A
git commit -m "add bashrc and gitconfig"
git push
Using Templates for Machine-Specific Config
Templates let the same source file render differently on different machines — your work laptop can get a different Git email than your personal one. chezmoi uses Go's text/template syntax.
Converting a file to a template
chezmoi add --template ~/.gitconfig
The source file is now named dot_gitconfig.tmpl. Open it:
chezmoi edit ~/.gitconfig
Replace static values with template variables pulled from chezmoi's data. chezmoi auto-populates data from ~/.config/chezmoi/chezmoi.toml.
Example dot_gitconfig.tmpl:
[user]
name = {{ .chezmoi.fullname }}
email = {{ .email }}
Define .email and other custom variables in your config file:
cat ~/.config/chezmoi/chezmoi.toml
[data]
email = "[email protected]"
You can also branch on the hostname or OS:
{{- if eq .chezmoi.hostname "work-laptop" }}
email = "[email protected]"
{{- else }}
email = "[email protected]"
{{- end }}
Run chezmoi apply to render the template and write the result to ~/.gitconfig. The template itself never lands in your home directory.
Keeping Secrets Out of the Repo
Secrets (API keys, tokens, passphrases) must never be stored in plain text in your dotfiles repo. chezmoi integrates natively with age (file encryption) and pass (the standard Unix password manager), among others.
Option A — Encrypting files with age
Install age and generate a key:
# Debian/Ubuntu
sudo apt install age
# Fedora
sudo dnf install age
# Arch
sudo pacman -S age
age-keygen -o ~/.config/chezmoi/key.txt
# Output: Public key: age1ql3z7hjy...
Tell chezmoi to use age and point it at the key and recipient (your public key):
cat ~/.config/chezmoi/chezmoi.toml
[encryption]
tool = "age"
age.identity = "~/.config/chezmoi/key.txt"
age.recipient = "age1ql3z7hjy..." # paste your public key here
Add a secret file — chezmoi encrypts it before writing to the source directory:
chezmoi add --encrypt ~/.ssh/id_ed25519
The source file becomes encrypted_dot_ssh/encrypted_private_id_ed25519.age. That encrypted blob is safe to commit. On apply, chezmoi decrypts it using your key file. Keep your key.txt out of the repo — back it up securely elsewhere.
Option B — Reading secrets from pass at template render time
If you use pass, reference secrets inline in templates:
export GITHUB_TOKEN={{ passFields "github/token" | get "password" }}
chezmoi calls pass when rendering the template, so the actual value is never stored in the source directory.
Bootstrapping a New Machine
This is where chezmoi pays off. On a brand-new system, a single command clones your repo, renders templates, decrypts secrets, and writes everything to the correct locations:
chezmoi init --apply https://github.com/youruser/dotfiles.git
If you want to review what will change before it touches anything:
chezmoi init https://github.com/youruser/dotfiles.git
chezmoi diff
chezmoi apply
For age-encrypted files, copy your key.txt to the new machine first (via encrypted USB, age-encrypted transfer, a secrets manager, etc.) before running apply.
Verification
Check that chezmoi's view of target state matches what's on disk:
chezmoi verify
If every managed file is up to date, the command exits silently with code 0. Any discrepancy is printed and exits non-zero — useful in CI or a post-login script.
See the managed file list:
chezmoi managed
Keeping Things Up to Date
Pull the latest commits from your remote and apply in one step:
chezmoi update
This is equivalent to git pull inside the source directory followed by chezmoi apply. Wire it into a systemd user timer or a shell login hook for fully automatic syncing if you prefer.
Troubleshooting
- Template rendering errors — run
chezmoi execute-templateon a single template file to see the error in isolation:chezmoi execute-template < ~/.local/share/chezmoi/dot_gitconfig.tmpl - age decryption fails on a new machine — the most common cause is a missing or wrong
key.txt. Confirm the path inchezmoi.tomlmatches where you copied the key. Double-check withage --decrypt -i ~/.config/chezmoi/key.txt somefile.age. - File shows as modified even after apply — this usually means the file has a different permission bit in the source. Use
chezmoi diffto confirm, then re-add it with the correct mode:chezmoi add --mode 600 ~/.ssh/config. - chezmoi not found after universal install — add
export PATH="$HOME/.local/bin:$PATH"to your~/.bashrcor~/.zshrcand reload.
Frequently asked questions
- Does chezmoi overwrite files I edit directly in my home directory?
- Only when you run 'chezmoi apply'. It will show you the diff first if you run 'chezmoi diff'. To pull changes from your home directory back into the source, use 'chezmoi re-add'.
- Can I use chezmoi without a public Git host like GitHub?
- Yes. The source directory is just a local Git repo. You can push to any Git server, a self-hosted Gitea instance, or keep it local-only with no remote at all.
- Is my age private key safe if my dotfiles repo is public?
- Yes, as long as you never add the private key file to the repo. Only the encrypted blobs and the public recipient key belong there. Back up key.txt out-of-band.
- What is the difference between 'chezmoi apply' and 'chezmoi update'?
- 'apply' renders and writes the current source state to disk. 'update' first pulls the latest commits from the remote, then calls apply — it is the normal day-to-day sync command.
- Can chezmoi run scripts when applying, for example to install packages?
- Yes. Files prefixed with 'run_' or 'run_once_' in the source directory are executed as scripts during apply. This lets you automate package installs or post-copy hooks.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.