Chezmoi Beginner Guide: Cross-Platform Dotfile Management
Overview
Chezmoi (pronounced "shay-mwa," French for "at my place") is a dotfile manager that keeps your personal configuration files consistent across every machine you use. Unlike simpler symlink-based tools, chezmoi copies files from a source directory to their correct locations, which means it can transform files on the fly using templates, encrypt secrets, and run setup scripts automatically.
If you manage dotfiles across macOS and Linux machines — possibly with different architectures like Apple Silicon and x86_64 — chezmoi handles the differences for you. You write one set of configuration files with optional conditional sections, and chezmoi figures out what belongs on each machine.
This guide walks you through installing chezmoi, adding your first dotfiles, pushing them to a git repository, and applying them on a second machine. By the end you will have a working cross-platform dotfiles setup that you can extend over time.
If you are new to dotfiles in general, start with [[dotfiles-beginner-guide]] for foundational concepts like symlinks, the XDG specification, and repository patterns. This guide assumes you understand what dotfiles are and focuses specifically on using chezmoi to manage them.
Prerequisites
Before starting, make sure you have the following ready:
- Git (version 2.20 or later) installed and configured with your name and email
- A GitHub or GitLab account with SSH access configured for pushing repositories
- A terminal you are comfortable working in — zsh, bash, or similar
- macOS (10.15+) or Linux as your operating system
- Homebrew (on macOS) or curl (on Linux) for installation
- Familiarity with basic dotfiles concepts from [[dotfiles-beginner-guide]]
Verify your setup:
git --version
# Expected: git version 2.39.0 or later
echo $SHELL
# Expected: /bin/zsh or /bin/bash
ssh -T git@github.com
# Expected: Hi username! You've successfully authenticated...
Key Concepts
Source State vs. Destination State
Chezmoi introduces a two-directory model:
- Source state: your dotfiles stored in
~/.local/share/chezmoi/(a git repository you control) - Destination state: the actual files on your system (e.g.,
~/.zshrc,~/.config/nvim/init.lua)
When you run chezmoi apply, chezmoi reads the source state, processes any templates, and writes the result to the destination. This is different from symlink tools like GNU Stow — chezmoi creates real files, not links.
Source: ~/.local/share/chezmoi/dot_zshrc
↓ chezmoi apply
Destination: ~/.zshrc
File Naming Conventions
Chezmoi uses special prefixes in filenames to indicate how files should be handled:
| Source filename | Destination | Meaning |
|---|---|---|
dot_zshrc | ~/.zshrc | dot_ becomes . |
dot_config/nvim/init.lua | ~/.config/nvim/init.lua | Nested directories work naturally |
dot_zshrc.tmpl | ~/.zshrc | .tmpl means it is a Go template |
private_dot_ssh/config | ~/.ssh/config (mode 0600) | private_ sets restrictive permissions |
run_once_install-packages.sh | (executed once) | run_once_ scripts run one time only |
encrypted_private_key.age | (decrypted on apply) | encrypted_ stores secrets safely |
You rarely type these names manually — chezmoi add creates them for you.
Templates for Cross-Platform Configs
Templates let you write one file that adapts to each machine. Chezmoi uses Go's text/template syntax:
{{ if eq .chezmoi.os "darwin" }}
# macOS-specific line
{{ else if eq .chezmoi.os "linux" }}
# Linux-specific line
{{ end }}
Chezmoi automatically provides variables like .chezmoi.os, .chezmoi.arch, .chezmoi.hostname, and you can define your own in a config file.
The Apply Workflow
The core workflow is always the same:
- Edit the source state (either directly or with
chezmoi edit) - Preview changes with
chezmoi diff - Apply changes with
chezmoi apply - Commit to git with
chezmoi git add . && chezmoi git commit
Step-by-Step Instructions
Step 1: Install Chezmoi
macOS (Homebrew):
brew install chezmoi
Linux (curl one-liner):
sh -c "$(curl -fsLS get.chezmoi.io)"
Linux (Homebrew on Linux):
brew install chezmoi
Verify the installation:
chezmoi --version
Expected output:
chezmoi version v2.47.1, commit ..., built at ...
Step 2: Initialize Chezmoi
Run the init command to create the source directory:
chezmoi init
This creates ~/.local/share/chezmoi/ as a new git repository. Inspect it:
ls -la ~/.local/share/chezmoi/
Expected output:
total 0
drwxr-xr-x 3 user staff 96 Apr 13 10:00 .
drwxr-xr-x 5 user staff 160 Apr 13 10:00 ..
drwxr-xr-x 9 user staff 288 Apr 13 10:00 .git
Step 3: Add Your First Dotfiles
Use chezmoi add to bring existing files under management:
# Add your shell config
chezmoi add ~/.zshrc
# Add git configuration
chezmoi add ~/.gitconfig
# Add an entire directory (neovim config)
chezmoi add ~/.config/nvim
# Add tmux config
chezmoi add ~/.config/tmux/tmux.conf
# Add sesh config
chezmoi add ~/.config/sesh/sesh.toml
# Add SSH config (chezmoi auto-detects private permissions)
chezmoi add ~/.ssh/config
Check what chezmoi is now managing:
chezmoi managed
Expected output:
.config/nvim/init.lua
.config/nvim/lua/config.lua
.config/sesh/sesh.toml
.config/tmux/tmux.conf
.gitconfig
.ssh/config
.zshrc
Step 4: Inspect the Source Directory
See how chezmoi stored your files:
ls -la ~/.local/share/chezmoi/
Expected output:
dot_config/
├── nvim/
│ ├── init.lua
│ └── lua/
│ └── config.lua
├── sesh/
│ └── sesh.toml
└── tmux/
└── tmux.conf
dot_gitconfig
dot_zshrc
private_dot_ssh/
└── config
Notice how ~/.zshrc became dot_zshrc and ~/.ssh/config became private_dot_ssh/config.
Step 5: Edit a Managed File
Use chezmoi edit to modify files in the source state:
chezmoi edit ~/.zshrc
This opens the source copy in your $EDITOR. Make a change (for example, add an alias), save, and close. Then preview and apply:
# See what would change
chezmoi diff
# Apply the changes
chezmoi apply -v
The -v flag shows verbose output so you can see exactly what was written.
Step 6: Push to a Remote Repository
Create a new empty repository on GitHub (e.g., dotfiles or dotfiles-chezmoi), then push:
# Navigate to the chezmoi source directory
chezmoi cd
# Add the remote
git remote add origin git@github.com:yourusername/dotfiles.git
# Commit everything
git add .
git commit -m "Initial chezmoi dotfiles: zsh, git, nvim, tmux, sesh, ssh"
# Push
git push -u origin main
# Return to your previous directory
exit
Alternatively, use chezmoi's built-in git passthrough:
chezmoi git -- remote add origin git@github.com:yourusername/dotfiles.git
chezmoi git add .
chezmoi git commit -- -m "Initial chezmoi dotfiles"
chezmoi git push -- -u origin main
Step 7: Apply on a New Machine
On a fresh macOS or Linux machine, run a single command:
chezmoi init --apply git@github.com:yourusername/dotfiles.git
This clones your repository, initializes chezmoi, and applies all your dotfiles in one step. Your shell config, git settings, editor setup, and terminal preferences are all instantly available.
If you prefer to preview first:
chezmoi init git@github.com:yourusername/dotfiles.git
chezmoi diff
chezmoi apply
Step 8: Keep Machines in Sync
When you change dotfiles on one machine, commit and push:
chezmoi git add .
chezmoi git commit -- -m "Update zsh aliases"
chezmoi git push
On another machine, pull and apply:
chezmoi update
The update command pulls the latest changes from the remote and applies them. If there are conflicts, chezmoi shows a diff and lets you resolve them with chezmoi merge.
Practical Examples
Example 1: Adding Shell Aliases That Work Everywhere
Create a .zshrc that works on both macOS and Linux:
chezmoi edit ~/.zshrc
Add platform-aware content. After editing, the source file at ~/.local/share/chezmoi/dot_zshrc might look like:
# Common aliases
alias ll='ls -lh'
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
# Editor
export EDITOR="nvim"
# Path additions
export PATH="$HOME/.local/bin:$PATH"
Apply and verify:
chezmoi apply
source ~/.zshrc
ll # Should work
Example 2: Adding Neovim Configuration
If your neovim config is a directory with multiple files:
chezmoi add ~/.config/nvim
This adds the entire directory tree. Any future edits:
chezmoi edit ~/.config/nvim/init.lua
chezmoi apply
Example 3: Managing Your Sesh Config
Your [[sesh-beginner-guide|sesh]] configuration lives at ~/.config/sesh/sesh.toml:
chezmoi add ~/.config/sesh/sesh.toml
chezmoi edit ~/.config/sesh/sesh.toml
Edit it to add your project sessions, then apply:
chezmoi apply -v
Example 4: First Template — OS-Specific PATH
Convert your .zshrc to a template to handle macOS vs. Linux differences:
# Convert to a template
chezmoi chattr +template ~/.zshrc
Now edit it:
chezmoi edit ~/.zshrc
The file is now dot_zshrc.tmpl. Add conditional sections:
# Common settings
export EDITOR="nvim"
alias ll='ls -lh'
{{ if eq .chezmoi.os "darwin" -}}
# macOS: Homebrew on Apple Silicon
export PATH="/opt/homebrew/bin:$PATH"
alias flush-dns='sudo dscacheutil -flushcache'
{{ else if eq .chezmoi.os "linux" -}}
# Linux: standard paths
export PATH="/usr/local/bin:$PATH"
alias open='xdg-open'
{{ end -}}
# Common PATH additions
export PATH="$HOME/.local/bin:$PATH"
Test the template output without applying:
chezmoi execute-template < ~/.local/share/chezmoi/dot_zshrc.tmpl
Then apply:
chezmoi diff
chezmoi apply
Example 5: Quick Status Check
See what has changed since your last apply:
chezmoi status
Output codes: A = added, M = modified, D = deleted, R = running script needed.
Hands-On Exercises
Exercise 1: Basic Setup (15 minutes)
- Install chezmoi on your current machine
- Run
chezmoi init - Add your
.zshrc(or.bashrc) and.gitconfig - Run
chezmoi managedto verify - Make a small change with
chezmoi edit ~/.zshrc - Preview with
chezmoi diff - Apply with
chezmoi apply -v - Commit:
chezmoi git add . && chezmoi git commit -- -m "First dotfiles"
Success criteria: chezmoi managed shows your files, and chezmoi diff shows no pending changes after apply.
Exercise 2: Push and Clone (15 minutes)
- Create a new repository on GitHub called
dotfiles - Push your chezmoi source to it (see Step 6 above)
- On a different machine (or a VM/container), run:
chezmoi init --apply git@github.com:yourusername/dotfiles.git
- Verify your shell config loaded: open a new terminal and check your aliases
Success criteria: Your aliases and editor config work on the second machine.
Exercise 3: Create Your First Template (10 minutes)
- Convert
.zshrcto a template:chezmoi chattr +template ~/.zshrc - Add an
{{ if eq .chezmoi.os "darwin" }}block with a macOS-only alias - Test with
chezmoi execute-template - Apply and verify the alias works (or does not appear on Linux)
Success criteria: chezmoi cat-config shows the rendered output appropriate for your OS.
Troubleshooting
"chezmoi: command not found"
Cause: Chezmoi is not installed or not in your PATH.
Solution:
# macOS
brew install chezmoi
# Linux — installs to ~/.local/bin
sh -c "$(curl -fsLS get.chezmoi.io)"
# Make sure ~/.local/bin is in your PATH
export PATH="$HOME/.local/bin:$PATH"
"chezmoi apply" shows unexpected diffs
Cause: The destination file was edited directly instead of through chezmoi edit.
Solution:
# See the diff
chezmoi diff
# Option 1: Accept the source version (overwrite local changes)
chezmoi apply
# Option 2: Re-add the local version to source
chezmoi re-add
Use chezmoi re-add when you intentionally edited the file outside of chezmoi and want to keep those changes.
Template syntax error
Cause: Malformed Go template syntax.
Solution:
# Test the template in isolation
chezmoi execute-template < ~/.local/share/chezmoi/dot_zshrc.tmpl
# Common mistakes:
# - Missing {{ end }} to close an if block
# - Using = instead of eq for comparison
# - Forgetting quotes around string values: eq .chezmoi.os "darwin"
"chezmoi add" refuses a file
Cause: The file is in a location chezmoi considers dangerous (e.g., root-owned files).
Solution:
# Use --force if you are sure
chezmoi add --force /path/to/file
# Or add it manually by creating the source file
chezmoi cd
mkdir -p dot_config/myapp
cp ~/.config/myapp/config dot_config/myapp/config
SSH key permissions are wrong after apply
Cause: Chezmoi created the file but did not set restrictive permissions.
Solution: Use the private_ prefix:
# Re-add with correct detection
chezmoi forget ~/.ssh/config
chezmoi add ~/.ssh/config
# chezmoi auto-detects .ssh and uses private_ prefix
# Verify
ls -la ~/.local/share/chezmoi/private_dot_ssh/
References
- Chezmoi Official Documentation: https://www.chezmoi.io/
- Chezmoi Quick Start: https://www.chezmoi.io/quick-start/
- Chezmoi GitHub Repository: https://github.com/twpayne/chezmoi
- Go Template Documentation: https://pkg.go.dev/text/template
- Chezmoi User Guide: https://www.chezmoi.io/user-guide/command-overview/
Related Tutorials
- [[dotfiles-beginner-guide|Dotfiles Beginner Guide]] — Foundational dotfiles concepts, symlinks, and repository patterns
- [[dotfiles-deep-dive|Dotfiles Deep Dive]] — Advanced dotfile management including GNU Stow, bare git repos, and architecture comparisons
- [[dotfiles-tool-questionnaire|Dotfiles Tool Questionnaire]] — The decision questionnaire that led to choosing chezmoi
- [[chezmoi-deep-dive|Chezmoi Deep Dive]] — Advanced chezmoi: templates, encryption, run scripts, and multi-machine strategies
- [[sesh-beginner-guide|Sesh Beginner Guide]] — Terminal session manager whose config is a great dotfile to manage
- [[sesh-deep-dive|Sesh Deep Dive]] — Advanced sesh configuration
- [[git-worktrees-worktrunk-beginner-guide|Git Worktrees Beginner Guide]] — Git worktrees for managing your dotfiles branches
- [[worktrunk-beginner-guide|Worktrunk Beginner Guide]] — Simplified git worktree management
- [[worktrunk-deep-dive|Worktrunk Deep Dive]] — Advanced worktree patterns
- [[macos-app-layout-beginner-guide|macOS App Layout Beginner Guide]] — Syncing
.bunchfiles and Moom exports across Macs - [[macos-app-layout-deep-dive|macOS App Layout Deep Dive]] — Advanced window-layout automation
- [[bunch-beginner-guide|Bunch Beginner Guide]] — Bunch fundamentals for macOS developers
- [[bunch-deep-dive|Bunch Deep Dive]] — Templating
.bunchfiles per host - [[moom-beginner-guide|Moom Beginner Guide]] — Window management basics
- [[moom-deep-dive|Moom Deep Dive]] — Per-host Moom snapshot management
- [[karabiner-elements-beginner-guide|Karabiner-Elements Beginner Guide]] — Karabiner config at
~/.config/karabineris a great chezmoi candidate - [[karabiner-elements-deep-dive|Karabiner-Elements Deep Dive]] — Per-device rules benefit from chezmoi templates across machines
Summary
Chezmoi gives you a single command to set up any new machine with your complete development environment. The key workflow is straightforward: chezmoi add to track files, chezmoi edit to make changes, chezmoi diff to preview, and chezmoi apply to write them. Push to git and run chezmoi init --apply on any new machine.
The main things to remember as you get started:
- Always use
chezmoi editinstead of editing destination files directly — this keeps source and destination in sync. - Use
chezmoi diffbeforechezmoi applyuntil you are confident in your setup. - Start simple — add your shell config and gitconfig first, then expand to editor and terminal configs.
- Templates come later — get comfortable with the basic add/edit/apply cycle before introducing
{{ if }}blocks. - Commit often — your chezmoi source directory is a git repo, so commit after every meaningful change.
When you are ready for templates, encrypted secrets, run scripts, and multi-machine strategies, continue to [[chezmoi-deep-dive|Chezmoi Deep Dive]].
Related Tutorials
- [[docker-test-container-beginner-guide|Docker Test Container Beginner Guide]] — test chezmoi-managed dotfiles safely in containers
- [[honeymux-beginner-guide|Honeymux Beginner Guide]] — tmux TUI wrapper whose config can be managed with chezmoi
- [[ssh-tutorial|SSH Tutorial]]
- [[ssh-config-deep-dive|SSH Config Deep Dive]]