Nam Hoai NguyenMy taking notes

Syncing dotfile Across Machines with Chezmoi

Stop reconfiguring your machine from scratch on every new machine.

Nam-Hoai Nguyen
added
updated

The problem

Every time, When I got a new Macbook / Linux Machine (an office work, a VPS), I end up spending hours setting up terminal from scratch. Installing zsh,
zinit
, git, configuring SSH keys, setting up plugins, aliases and shell functions…
I tried solving this with a bootstrap script. It worked well enough, it installed everything: brew, git, docker , etc. But installing programs is only half the battle. It didn't restore my configuration files. I still had to set up nvm, and redefine all my aliases and functions from scratch every time.
With GNU Stow, configuration files are simply symlinked, allowing immediate config updates. However, this introduces an annoying issue: authentication files for CLI tools like Claude Code and GitHub Copilot are also stored within the symlinked directories. Consequently, every time I push to Git, I am forced to review the changes to ensure no secrets are included. On several occasions, I have accidentally pushed these sensitive files to GitHub and had to reset those commits.
The other pain point: when I updated a config on one machine, I had to manually mirror that change on all the others. If I forgot, the machines would quietly drift apart, and I'd end up with different behavior depending on where I was working

Introducing chezmoi

Chezmoi is an open-source tool managing your dotfiles across multiple machine, securely.
The concept is simple: you tell chezmoi which config files to tack, and it stores them in a Git repository that you push to Github (or any remote). Frome there, syncing your config to a new machine is just a matter of cloning and applying.
On a new machine, I either run the one line:
1chezmoi init --apply $GITHUB_USERNAME
2
3# or
4chezmoi init --apply https://github.com/$GITHUB_USERNAME/dotfiles
Or after my bootstrap script install everything:
1chezmoi init
2chezmoi apply
When I update a config on a config on one machine:
1chezmoi add ~/.zshrc
2chezmoi cd 
3git commit -m 'chore: update zsh config'
4git push
Automatically commit and push changes to your repo:
1[git]
2    autoCommit = true
3    autoPush = true
~/.config/chezmoi/chezmoi.toml
On another machine will pull the latest changes from your repo
1chezmoi git pull -- --autostash --rebase && chezmoi diff
If you happy with changes, then you can run to apply them:
1chezmoi apply
Restart the terminal and it’s done. This has been especially useful when spinning up AWS EC2 instances. What used to take hours now takes minutes.

Architecture Design

Repository Structure

1├── ...
23├── home/                   # dotfiles under chezmoi management
4│   ├── dot_zshrc          # - deployed as ~/.bashrc
5│   ├── dot_vimrc           # - deployed as ~/.vimrc
6│   ├── dot_config/         # - deployed as ~/.config/
7│   └── .chezmoi.yaml.tmpl  # - chezmoi configuration file
89├── install/                # setup scripts (testable)
10│   ├── common/             # - common installation scripts
11│   ├── macos/              # - macOS-specific scripts
12│   └── ubuntu/             # - Ubuntu-specific scripts
1314├── tests/                  # automated tests with Bats
15│   ├── install/            # - tests for installation scripts
16│   └── files/              # - tests for files after chezmoi deployment
1718└── ...
I’m sure there are other ways to manage dotfiles , but this is what’s been working for me. Let me known how you handle yours.