Skip to content
Go back

chezmoi vs GNU Stow vs Bare-Repo Dotfiles

By SumGuy 10 min read
chezmoi vs GNU Stow vs Bare-Repo Dotfiles
Contents

You’ve Been Hand-Editing Dotfiles on Each Machine. Stop That.

Your ~/.bashrc is slightly different on your laptop, desktop, and server. Your Neovim config has that one tweak you made at 2 AM that you can never quite remember. Your Caddy config exists only on the production box because you rebuilt it six months ago and forgot to save it anywhere.

This is how people lose their configs. Not to catastrophe, but to entropy.

Dotfile managers solve this problem in radically different ways. Three contenders keep showing up in /r/homelab and Discord: chezmoi, GNU Stow, and the bare git repo. Each one is correct for someone, and wrong for someone else. Here’s how to pick, and why each approach exists.


The Three Approaches

Stow is the oldest of the three. It’s been around since the 1990s. It does one thing and does it well: symlink files from a dotfiles repo into your home directory.

Init:

Terminal window
mkdir -p ~/dotfiles
cd ~/dotfiles
git init
mkdir -p config/nvim config/zsh
# Copy your existing configs into the tree matching home structure
cp ~/.config/nvim/init.lua config/nvim/
cp ~/.zshrc config/zsh/
git add .
git commit -m "Initial dotfiles"

Install on a new machine:

Terminal window
cd ~
git clone <your-repo> dotfiles
cd dotfiles
stow config # symlinks ~/.config/nvim -> ~/dotfiles/config/nvim

What Stow actually does: It mirrors your home directory tree structure inside the repo, then creates symlinks from ~ to the repo files. If you have dotfiles/config/nvim/init.lua, Stow creates ~/.config/nvim/init.lua~/dotfiles/config/nvim/init.lua.

Pros:

Cons:

When Stow shines:


2. chezmoi: Templating and Secrets

chezmoi is the newcomer. It treats your dotfiles like infrastructure-as-code. You define templates, variables, and secrets, and chezmoi renders them during install.

Init:

Terminal window
chezmoi init
# This creates ~/.local/share/chezmoi/ — the source directory
cd ~/.local/share/chezmoi
# Add your configs as templates:
chezmoi add ~/.zshrc
chezmoi add ~/.config/nvim/init.lua
chezmoi add ~/.ssh/config # chezmoi will prompt you to template secrets

What you see in the source repo:

Terminal window
~/.local/share/chezmoi/
dot_zshrc
dot_config/nvim/init.lua
dot_ssh/encrypted_config
.chezmoi.yaml.tmpl

The dot_ prefix is chezmoi’s syntax for “this should be a file in ~”. It’s stripped during apply.

Template example:

.chezmoi.yaml.tmpl
hostname: {{ .hostname }}
os: {{ .chezmoi.os }}

Then in your files:

Terminal window
# ~/.zshrc template
{{ if eq .chezmoi.os "darwin" }}
export HOMEBREW_PREFIX="/opt/homebrew"
{{ end }}
export EDITOR="nvim"

Install on a new machine:

Terminal window
chezmoi init <your-repo>
chezmoi apply # renders templates, decrypts secrets, applies everything

Pros:

Cons:

When chezmoi shines:


3. Bare Git Repo: The Minimalist’s Choice

The bare repo approach is the anti-tool. You create a bare git repo in a hidden directory and use a shell alias to manage it like a normal repo—except you don’t need to cd into a dotfiles folder. Everything lives in ~.

Init:

Terminal window
git init --bare ~/.dotfiles
alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
dotfiles config --local status.showUntrackedFiles no
dotfiles add ~/.zshrc ~/.config/nvim/init.lua
dotfiles commit -m "Initial dotfiles"
dotfiles remote add origin <your-repo>
dotfiles push -u origin main

Install on a new machine:

Terminal window
alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
git clone --bare <your-repo> ~/.dotfiles
dotfiles checkout main

What’s happening: You have a hidden bare repo (.dotfiles/) that tracks files scattered across your entire home directory. The alias lets you run git commands as if you’re in ~, but the actual repo metadata lives in .dotfiles/.

Pros:

Cons:

When bare repos shine:


Workflow Comparison: A Day in the Life

Scenario: You update your Neovim config on your laptop, then need it on your desktop 30 minutes later.

GNU Stow:

Terminal window
# On laptop: edit ~/dotfiles/config/nvim/init.lua
cd ~/dotfiles
git add config/nvim/init.lua
git commit -m "Add indent settings"
git push
# On desktop:
cd ~/dotfiles
git pull
# Done. Symlinks already point to the right place.

chezmoi:

Terminal window
# On laptop: edit a config, then apply
chezmoi edit ~/.config/nvim/init.lua # opens in $EDITOR
chezmoi apply
chezmoi git add -A && chezmoi git commit -- -m "Add indent settings" && chezmoi git push
# On desktop:
chezmoi update
# chezmoi pulls, rerenders templates, applies changes

Bare repo:

Terminal window
# On laptop: edit ~/.config/nvim/init.lua directly
dotfiles add ~/.config/nvim/init.lua
dotfiles commit -m "Add indent settings"
dotfiles push
# On desktop:
dotfiles pull
# Done. Files are already in place.

All three get you there. Bare repo and Stow are fastest. chezmoi adds the templating step but gains multi-machine safety.


Secrets: The Critical Difference

Let’s say you have an SSH key or an API token that needs to live in your dotfiles.

GNU Stow: Don’t. Seriously. Either:

chezmoi:

Terminal window
chezmoi add ~/.ssh/id_ed25519
# chezmoi detects it's a sensitive file (or you mark it encrypted_*)
# and encrypts it with GPG/age
chezmoi git add -A && chezmoi git commit -- -m "Add encrypted ssh key" && chezmoi git push
# The key is encrypted at rest. Safe to push.
# On another machine with your GPG key:
chezmoi apply
# chezmoi decrypts on apply using your GPG key

Bare repo: Same as Stow. You need git-crypt or manual secret management. Not built-in.

If you have secrets, chezmoi wins by default. Bare repo and Stow force you to do extra work.


Multi-Machine Sync: The Real Test

You have a laptop (macOS), a desktop (Ubuntu), and a Raspberry Pi (Raspberry Pi OS). They all need the same Neovim config, but:

GNU Stow: You need conditional symlinks or script wrappers. It can work, but you’re fighting the tool. Typically you’d create separate branches per machine or manually exclude files.

chezmoi: This is exactly what it’s built for.

.chezmoi.yaml.tmpl
data:
hostname: {{ .chezmoi.hostname }}
os: {{ .chezmoi.os }}
username: {{ .chezmoi.username }}

Then in your configs:

Terminal window
{{ if eq .chezmoi.os "darwin" }}
export HOMEBREW_PREFIX="/opt/homebrew"
{{ else if eq .chezmoi.os "linux" }}
export HOMEBREW_PREFIX="/usr/local"
{{ end }}

Files can be templated or entirely skipped per machine.

Bare repo: Branches or manual exclusion lists. Workable, but tedious. You’d probably end up with a ~/.machine-config directory that you source into shell configs based on hostname.


The Decision Tree

Use GNU Stow if:

Use chezmoi if:

Use bare repo if:


The Honest Take

Here’s the thing: all three work. I’ve shipped production configs with each. Stow is stable and ancient. chezmoi is modern and solves the hard problems. Bare repo is minimalist and honest.

If you’re starting today and have more than one machine, pick chezmoi. The learning curve is real, but you’ll save yourself three months of manual sync pain. If you’re a bare-repo person, you already know it and you’re not reading this article.

Stow wins if you want to introduce one coworker to dotfile management without teaching them git-crypt or templates. It’s the gateway drug.

Stop hand-editing files on each machine. Pick one, set it up once, and let your future self thank you at 2 AM when you need to restore a borked config.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
Firefly III vs Actual vs Fava (Beancount)

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts