devAlice
← Mac

Dotfiles Management — chezmoi vs yadm vs raw symlinks, Mac Edition

Comparing tools to cleanly replicate the same dev environment across machines, plus a practical chezmoi setup.

If you still copy-paste .zshrc, .gitconfig, and ~/.config/... files every time you get a new Mac, you need a dotfiles management tool. Set it up once, and a new machine restores its full configuration with a single command.

This guide compares three approaches (chezmoi · yadm · raw symlink + Git) and walks through the recommended chezmoi setup in detail.

TL;DR

ToolStrengthsWeaknessesWhen to use
chezmoi (recommended)Templates / per-machine branching / secrets integration / bidirectional applyLearning curve (Go templates)1+ machine, mixed OS
yadmGit wrapper, zero learning costWeak per-machine branchingSimple single-OS
raw symlink + GitZero dependencies, transparentYou write everything yourselfMinimalist

1. Why a Dotfiles Tool

Putting .zshrc directly in a Git repo and symlinking it works. But these problems accumulate:

  • Per-machine differences: Work Mac's git user.email vs personal. Hard-coded branching breaks.
  • Secrets: API keys / SSH config mixed in is dangerous. Easy to forget .gitignore.
  • Bidirectional: If you edit ~/.zshrc directly, the repo drifts and you lose track of which is source of truth.
  • New machine: clone → install deps → symlink → permissions… every time.

A dotfiles tool wraps this into a consistent workflow.

2. Options Compared

chezmoi

Go-based CLI. Template engine (per-machine branching), secrets (1Password / Bitwarden integration), bidirectional (chezmoi diff / chezmoi apply).

yadm

A Git wrapper that treats dotfiles in ~ as a direct Git repository. Zero learning cost. Per-machine branching is supported but limited (alt files like .zshrc##os.Darwin).

Raw symlink + Git

Put files in ~/dotfiles/ and symlink with an install script. Most transparent, but you build every feature yourself.

Recommendation: chezmoi

For a single operator with 1–3 machines, chezmoi wins decisively. A one-hour learning investment pays back for life.

3. chezmoi Setup

3.1 Install

brew install chezmoi

3.2 Prepare a GitHub Repo

Create a private dotfiles repo on GitHub (name is your choice). Empty is fine.

3.3 Initialize chezmoi

chezmoi init github.com/yourname/dotfiles
# Or, if SSH is available:
chezmoi init git@github.com:yourname/dotfiles.git

~/.local/share/chezmoi/ becomes the working tree (the actual Git repo).

3.4 Add Files

# Put .zshrc under chezmoi management
chezmoi add ~/.zshrc
 
# Or multiple at once
chezmoi add ~/.gitconfig ~/.tmux.conf ~/.config/starship.toml

Files are copied as ~/.local/share/chezmoi/dot_zshrc (leading dot → dot_ prefix).

3.5 Commit + Push

chezmoi cd          # jump to the work tree
git add .
git commit -m "init dotfiles"
git push -u origin main
exit                # back to where you were

3.6 Restore on a New Machine

# On the new Mac
brew install chezmoi
chezmoi init --apply github.com/yourname/dotfiles

Done. All dotfiles materialize in ~ and apply immediately.

4. Per-Machine Branching (Templates)

chezmoi's strongest feature. The same .gitconfig adapts to work vs personal:

~/.local/share/chezmoi/dot_gitconfig.tmpl

Contents:

[user]
    name = Your Name
{{- if eq .chezmoi.hostname "Work-MacBook" }}
    email = me@company.com
    signingkey = AAAA....
{{- else }}
    email = personal@example.com
{{- end }}
 
[core]
    editor = nvim
    autocrlf = input

The .tmpl extension marks a template. chezmoi branches based on hostname when applying.

4.1 Available Variables

chezmoi data       # print all context variables

Commonly used:

  • .chezmoi.osdarwin / linux / windows
  • .chezmoi.hostname — machine name
  • .chezmoi.archarm64 / amd64
  • Custom — add with chezmoi edit-config

4.2 OS-Specific Files

Rename dot_zshrc to dot_zshrc.tmpl and branch by OS:

{{ if eq .chezmoi.os "darwin" -}}
# macOS only
export HOMEBREW_PREFIX="/opt/homebrew"
eval "$($HOMEBREW_PREFIX/bin/brew shellenv)"
{{ else if eq .chezmoi.os "linux" -}}
# Linux only
export PATH="$HOME/.linuxbrew/bin:$PATH"
{{ end -}}
 
# Shared
alias ll='ls -lah'

5. Secrets Integration

Never write API keys or tokens directly in dotfiles. Use chezmoi's secret-tool integration:

1Password CLI

brew install --cask 1password 1password-cli
op signin

dot_ssh/config.tmpl:

Host github.com-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal
  PreferredAuthentications publickey

dot_env.tmpl referencing secrets:

export ANTHROPIC_API_KEY={{ onepasswordRead "op://Private/Anthropic/api_key" }}
export GITHUB_TOKEN={{ onepasswordRead "op://Private/GitHub/token" }}

When you chezmoi apply, it fetches values from 1Password and writes a plaintext .env. That plaintext file is .gitignored, so it never enters the repo.

Bitwarden / pass / age

Each has official chezmoi integration — see docs.chezmoi.io.

6. Workflow

Daily

# After editing ~/.zshrc directly
chezmoi diff       # see what changed
chezmoi add ~/.zshrc   # propagate back to the repo
chezmoi cd && git commit -am "tweak zshrc" && git push

Sync Another Machine

chezmoi update     # git pull + apply

Preview Changes

chezmoi diff       # changes before applying
chezmoi apply -v   # verbose apply

Edit Source

chezmoi edit ~/.zshrc   # edit source (= repo) and auto-apply

7. yadm — The Lightest Alternative

If zero learning cost is your priority, yadm:

brew install yadm
yadm init
yadm add ~/.zshrc
yadm commit -m "init"
yadm remote add origin git@github.com:yourname/dotfiles.git
yadm push -u origin main
 
# Other machine
yadm clone git@github.com:yourname/dotfiles.git

Commands are identical to git (only the binary differs). Drawback: per-machine branching is limited to alt files like .zshrc##os.Darwin.

8. Raw Symlink + Git

Minimum dependencies:

mkdir ~/dotfiles && cd ~/dotfiles
git init
mv ~/.zshrc ~/dotfiles/zshrc
ln -s ~/dotfiles/zshrc ~/.zshrc
 
# install.sh
#!/usr/bin/env bash
for f in zshrc gitconfig tmux.conf; do
  ln -sf "$HOME/dotfiles/$f" "$HOME/.$f"
done

Clean, but you implement secrets, branching, and bootstrap yourself. Most people migrate to chezmoi within a year.

How to Verify

  1. chezmoi diff → no changes (everything applied)
  2. Edit ~/.zshrc directly → chezmoi diff shows the diff
  3. On a new machine (or clean VM) chezmoi init --apply github.com/you/dotfiles → all dotfiles restored
  4. After modifying a template, chezmoi apply -v → different output based on hostname
  5. With 1Password integration, chezmoi apply writes plaintext to ~/.env, but the repo doesn't contain it

Troubleshooting

chezmoi apply doesn't change anything

  • Check chezmoi diff. If empty, that's expected.
  • File permission differences can hide changes — try chezmoi apply --force.

Template syntax errors

{{ ... }} is Go templates. Missing closing -}} and variable typos are common. Dry-run with chezmoi execute-template < dot_gitconfig.tmpl.

1Password integration fails

  • After op signin, session is valid for ~30 minutes by default. Run eval $(op signin) again.
  • Enable 1Password explicitly in chezmoi.toml (chezmoi edit-config).

Missing SSH keys on a new machine

Never store SSH keys themselves in dotfiles (security). Generate new keys on the new machine and register them with GitHub. chezmoi should only manage configs like ~/.ssh/config.

~/.zshrc doesn't update via direct edit

Use chezmoi edit ~/.zshrc (edits source, auto-applies). Or edit source directly and chezmoi apply.

References

Changelog

  • 2026-05-12: First draft. Three approaches compared + chezmoi practical setup (templates / secrets / workflow) + five troubleshooting cases.

Comments