Getting Started with Dotfile Management#
Up until now, I’ve mostly been working with GUI-driven IDEs on Windows. I wrote about why I’d like to shift to the terminal — the short version is that a portable, keyboard-driven workflow matters when you’re building solo. But alongside that shift, something else has been happening. Since Docker entered my daily work, I’ve been spending more and more time on Linux again — something I hadn’t done seriously since studying computer science. Linux stepped back into my life gradually, and for a long time I just used it without thinking much about it. I never cared about the fundamentals.
Until now. If I’m going to make the terminal my home, I need to start from the ground up. And that starts with something deceptively simple: managing my configuration files.
The Quake 2 Lesson#
Back in the late 90s, I used to play Quake 2 with custom keyboard bindings. I had my movement keys remapped, my weapon switches optimized — the whole setup was muscle memory. It made me fast. Then one day I went to a friend’s place to play, and without my bindings I was completely lost. My fingers reached for keys that did the wrong things. I played terribly. At the time, the only way to bring my config along would have been a floppy disk or a ZIP drive — not exactly something you carry to a casual gaming session.
The lesson was clear: custom bindings are a massive efficiency boost, but if you can’t take them with you, you’re worse off than someone who never customized at all. So I made a decision that stuck with me for years. I stopped customizing. I played with default bindings on every machine, every game. Worst case, I had to reset a friend’s custom config back to defaults — done. I was never the fastest player, but I was consistently decent everywhere.
The Same Problem, Twenty Years Later#
Now I’m facing the exact same issue with Linux, except the stakes are higher than a Quake 2 match. I work across multiple machines — a WSL2 instance on my Windows laptop, a Debian VM on my homelab, and soon dev containers that get created and destroyed regularly. If I customize my shell, my editor, my terminal — and I can’t take that configuration with me — I’m back to being lost on a friend’s computer.
But times have changed. If I’d had an internet-based mechanism to sync my Quake 2 config in 1999, I would have set up any computer in minutes — custom bindings, favorite avatar, everything. That mechanism exists now. There’s no reason to repeat the old mistake of avoiding customization out of fear of losing it.
So the plan is simple: build a setup that I can recover everywhere in minutes. To get there, I need to learn dotfile management.
I looked into a few solutions — GNU Stow, bare git repos, and chezmoi. Chezmoi stood out because it handles templating, secrets, and multi-machine differences out of the box. It felt like the right tool for where I’m heading, even if I’m starting simple.
Setting Up Chezmoi#
Installing chezmoi is a one-liner, but there’s a prerequisite: you need a dotfiles repository. So I created a new GitHub organization — knuth-info — as a home for everything I’m discovering through this blog. The first repo under it: dotfiles.
With the repo in place, getting chezmoi running on both my WSL2 instance and my Debian VM was exactly one command, straight from the chezmoi documentation:
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply knuth-infoEasy, right? But now — what do I actually do with it?
The Detour Into Zsh#
My first instinct was to add my .bashrc. But wait — I’d already decided I wanted to switch to zsh as part of the terminal-native transition. Why start managing bash configs when I’m about to abandon bash? So instead of adding my first dotfile, I installed zsh on both machines:
sudo apt install zshAnd switched to it as my default shell:
chsh -s $(which zsh)After reconnecting, zsh greeted me with its setup prompt. I skipped it, and there it was: an ugly, bare prompt with no history, no completions, nothing. Now what? How does zsh even work? Which files does it read? How do I customize it?
This turned into a full zsh exploration. I watched videos, read blog posts, looked at other people’s configs. It was overwhelming — zsh has an enormous ecosystem of frameworks, plugins, and themes. I decided to ignore all of that and start with the absolute minimum:
# History
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt SHARE_HISTORY
setopt HIST_IGNORE_DUPS
# Completion
autoload -Uz compinit && compinit
# Prompt (simple)
PROMPT='%n@%m %~ %# 'That was enough. The prompt showed me where I was, history worked across sessions, and tab completion did its job. Nothing fancy — just the basics I was used to from a default bash prompt.
First Dotfile, First Problem#
Now I finally had something to manage. Time to add it to chezmoi:
chezmoi add ~/.zshrc
chezmoi cd
git add -A && git commit -m "add zshrc" && git pushAnd… nothing happened. The push just hung.
I work with multiple accounts across multiple git servers — GitHub, GitLab, and Gitea — each with its own SSH identity. My global git config pointed to the wrong identity for this new repo. The chezmoi-managed local repo needed its own git configuration:
chezmoi cd
git config user.name "Marcus Knuth"
git config user.email "github@knuth.info"
git config core.sshCommand "ssh -i ~/.ssh/my_new_github_accounts_ssh_key"That fixed it. My first dotfile was pushed, and the repo was live.
The PATH Surprise#
But switching to zsh had another consequence I didn’t see coming. Bash reads .profile on login, which is where ~/bin gets added to the PATH. Zsh doesn’t read .profile — it has its own file for that: .zshenv. Since chezmoi installs itself into ~/bin, it suddenly wasn’t on my PATH anymore. Chezmoi, the tool I’d just set up to manage my environment, was broken by my environment change.
After some searching, the fix was straightforward. I created a .zshenv:
[[ -d "$HOME/bin" ]] && export PATH="$HOME/bin:$PATH"And added it to chezmoi — though I had to use the full path since chezmoi wasn’t in my PATH at that point:
~/bin/chezmoi add ~/.zshenv
~/bin/chezmoi cd
git add -A && git commit -m "add zshenv" && git pushThat was the last time I had to type that full path.
What’s Next#
Two dotfiles. That’s what I have so far — a .zshrc and a .zshenv. It’s not much, but the foundation is in place: chezmoi is running, the repo is syncing, and zsh is my default shell on both machines.
Now the real exploration begins. I want to learn about chezmoi’s auto-commit feature so I don’t have to manually push every change. I want to customize zsh properly — a plugin manager, a prompt that actually tells me something useful, aliases that save me keystrokes every day. And to make sure all of this actually works as advertised — that I can truly spin up my environment anywhere in minutes — I’m going to test it by working in throwaway containers for a while.
But that’s a story for the next post.



