dotfiles
My development environment as one idempotent script — fresh machine to configured in four minutes.
- Section
- dev tools
- Year
- 2024
- Role
- Solo project
- Stack
- Bash, Nix, Neovim
Replace this file with a real project. Small tools count — a well-explained 300-line script says more about how you think than a large project described in one sentence.
The problem
I set up a new laptop roughly once a year and spent most of a day on it every time, rediscovering which settings I cared about by noticing their absence.
What I chose, and why
One entry point, ./bootstrap, safe to run repeatedly. Everything it does is
idempotent: it checks the desired state, and acts only if reality differs.
I went with Nix for packages and plain symlinks for configuration, rather than going all-in on home-manager. Nix gives reproducible tool versions, which is the part I actually wanted. Config files stay as ordinary dotfiles I can edit in place and see under version control without a rebuild step — the friction of that rebuild is what made me abandon a fully-declarative setup the first time.
The hard part
Secrets. The repo is public, but the environment needs SSH keys, API tokens, and a work-specific Git identity.
The answer was that the repo contains no secrets and no encrypted secrets
either — it contains the shape of them. bootstrap generates a template at
~/.config/local/env, prompts for anything missing on first run, and every
subsequent run leaves it alone. The public repo describes the environment; the
machine holds the values.
What I’d change
The Neovim config is still 900 lines of Lua that I’ve never fully re-read. It works, and roughly 200 of those lines are for a workflow I abandoned in 2023.
I’d also add a test. A GitHub Action that runs bootstrap twice in a clean
container — asserting the second run makes no changes — would have caught the
three separate occasions I broke idempotency without noticing.