Ik gebruik al jaren dezelfde .zshrc. En mijn .vimrc. En mijn tmux config. In de loop der tijd zijn ze uitgegroeid tot een zorgvuldig afgestemd systeem dat precies doet wat ik wil. Het probleem: ik heb meerdere machines. Een laptop, een desktop, soms een VM om te testen. En alles gesynchroniseerd houden was altijd… geïmproviseerd.
De juiste dotfile management oplossing vinden heeft me jaren gekost. Ik heb alles geprobeerd. En ik bedoel alles.
De Lange Zoektocht
Het begon met de klassieker: een bare git repo in mijn home directory. git init --bare ~/.dotfiles, wat aliassen, klaar. Het werkt, maar het is fragiel. Één verkeerde git clean en je hebt je configs weggevaagd. En succes met machine-specifieke instellingen.
Toen ontdekte ik GNU Stow. Elegant idee: organiseer je dotfiles in directories, symlink ze naar de juiste plek. Ik heb dit een hele tijd gebruikt. Maar Stow heeft geen concept van templates. Mijn werk-laptop en persoonlijke desktop hebben verschillende .gitconfig instellingen nodig. Met Stow eindig je met gitconfig-work en gitconfig-personal en handmatig wisselen. Niet ideaal.
Op een gegeven moment dacht ik: “Ik gebruik al Ansible voor mijn servers, waarom niet voor mijn dotfiles?” Dus bouwde ik een Ansible playbook voor mijn workstation setup. Compleet met roles, templates, handlers — het hele pakket. Het was prachtig. Het was ook massive overkill. ansible-playbook draaien elke keer dat ik mijn .zshrc veranderde voelde belachelijk. En de YAML-soep voor simpele file operations was pijnlijk.
Ik heb ook geprobeerd:
- yadm — git-based, beter dan bare repo, maar templating voelde er opgeplakt
- dotbot — simpele YAML config, maar beperkte flexibiliteit
- home-manager (Nix) — krachtig, maar ik wilde niet full Nix gaan
Niks klikte echt. Elke tool loste sommige problemen op maar creëerde andere.
Tot ik chezmoi ontdekte en het combineerde met mise. Deze combo is mijn definitieve dotfile oplossing geworden. Eindelijk.
Het Probleem
Dotfile management lijkt simpel. Het zijn gewoon config bestanden, toch? Maar dingen worden snel ingewikkeld:
- Machine-specifieke config: Mijn laptop heeft andere instellingen nodig dan mijn desktop
- Secrets: Mijn
.gitconfigbevat tokens, mijn.ssh/configbevat hostnames die ik niet in een publieke repo wil - Tool versies: Ik heb Go 1.21 nodig op de ene machine en 1.22 op de andere
- Bootstrap: Op een verse machine wil ik binnen minuten productief zijn, niet uren
Een simpele git repo lost dit niet op. Je hebt templating, secret management en een bootstrap proces nodig.
Enter chezmoi
Chezmoi is een dotfile manager die al deze problemen oplost. De naam is Frans voor “bij mij thuis” (chez moi), wat passend is — het brengt je home environment overal mee naartoe.
Kernconcept
Chezmoi onderhoudt een “source state” (in ~/.local/share/chezmoi/) en past die toe op je home directory. De source state kan bevatten:
- Gewone bestanden
- Templates (met variabelen per machine)
- Scripts (voor dingen die niet in bestanden te vangen zijn)
- Versleutelde bestanden (voor secrets)
Aan de Slag
# Installeren
brew install chezmoi # macOS
sudo pacman -S chezmoi # Arch
sudo apt install chezmoi # Debian/Ubuntu
# Initialiseren
chezmoi init
# Je eerste bestand toevoegen
chezmoi add ~/.zshrc
Dat is het. Je .zshrc wordt nu beheerd door chezmoi.
De Basis Workflow
# Bestand toevoegen aan chezmoi
chezmoi add ~/.config/nvim/init.lua
# Bestand bewerken (opent in $EDITOR)
chezmoi edit ~/.zshrc
# Bekijk wat er zou veranderen
chezmoi diff
# Pas wijzigingen toe op home directory
chezmoi apply
# Push naar git
chezmoi cd
git add -A && git commit -m "Update zshrc" && git push
Templating: Machine-Specifieke Config
Dit is waar chezmoi schittert. Je kunt Go templates gebruiken om verschillende config per machine te hebben.
Definieer eerst je data in ~/.config/chezmoi/chezmoi.toml:
[data]
hostname = "laptop"
work = true
email = "tom@example.com"
Gebruik het dan in templates. Hernoem een bestand met .tmpl suffix:
chezmoi add --template ~/.gitconfig
Nu ~/.local/share/chezmoi/dot_gitconfig.tmpl:
[user]
name = Tom Meurs
email = {{ .email }}
{{ if .work -}}
[url "git@gitlab.work.com:"]
insteadOf = https://gitlab.work.com/
{{- end }}
Op mijn werk-laptop wordt de GitLab URL rewrite meegenomen. Op mijn persoonlijke machines niet.
Machine Detectie
Je kunt machine-eigenschappen automatisch detecteren:
# ~/.config/chezmoi/chezmoi.toml
[data]
hostname = {{ .chezmoi.hostname | quote }}
os = {{ .chezmoi.os | quote }}
{{ if eq .chezmoi.hostname "work-laptop" -}}
work = true
{{- else -}}
work = false
{{- end }}
Of gebruik een interactieve prompt bij eerste setup:
# chezmoi.toml.tmpl
[data]
email = {{ promptString "email" | quote }}
work = {{ promptBool "work machine" }}
Secrets met age
Commit nooit secrets in plain text. Chezmoi integreert met age voor encryptie.
# Genereer age key
age-keygen -o ~/.config/chezmoi/key.txt
# Configureer chezmoi om het te gebruiken
chezmoi edit-config
encryption = "age"
[age]
identity = "~/.config/chezmoi/key.txt"
recipient = "age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Nu kun je gevoelige bestanden versleutelen:
chezmoi add --encrypt ~/.ssh/config
Het bestand wordt versleuteld opgeslagen in je repo. Alleen machines met de age key kunnen het ontsleutelen.
Scripts: Wanneer Bestanden Niet Genoeg Zijn
Soms moet je commando’s uitvoeren. Chezmoi ondersteunt scripts:
# ~/.local/share/chezmoi/run_once_install-packages.sh
#!/bin/bash
# Dit draait één keer (bijgehouden via hash)
if command -v brew &> /dev/null; then
brew install ripgrep fd bat fzf
elif command -v apt &> /dev/null; then
sudo apt install -y ripgrep fd-find bat fzf
elif command -v pacman &> /dev/null; then
sudo pacman -S --noconfirm ripgrep fd bat fzf
fi
Script naamgeving:
run_once_— draait één keer, daarna nooit meer (bijgehouden via content hash)run_onchange_— draait wanneer de script inhoud verandertrun_— draait elke keer
Enter mise
Chezmoi handelt dotfiles af, maar hoe zit het met tool versies? Ik heb specifieke versies nodig van Go, Node, Python, Terraform, etc. En die moeten consistent zijn over machines heen.
Dit is waar mise binnenkomt. Het is een polyglot version manager — denk asdf, maar sneller en ergonomischer.
Waarom mise?
Ik heb jarenlang asdf gebruikt. Het werkt, maar:
- Traag (shell scripts)
- Plugin ecosysteem is gefragmenteerd
- Geen native support voor config in dotfiles
Mise is:
- Snel (geschreven in Rust)
- Ingebouwde support voor de meeste tools
- Config leeft in
~/.config/mise/config.toml— perfect voor chezmoi
Aan de Slag met mise
# Installeren
curl https://mise.run | sh
# Of via package manager
brew install mise
Toevoegen aan je shell:
# ~/.zshrc
eval "$(mise activate zsh)"
Definieer Je Tools
# ~/.config/mise/config.toml
[tools]
go = "1.22"
node = "lts"
python = "3.12"
terraform = "1.7"
kubectl = "latest"
helm = "latest"
Voer nu uit:
mise install
Alle tools worden geïnstalleerd op de opgegeven versies. Wanneer je van machine wisselt, geeft mise install je exact dezelfde omgeving.
Project-Specifieke Versies
Je kunt ook per-project versies hebben:
# ~/projects/legacy-app/.mise.toml
[tools]
node = "16" # Oud project heeft Node 16 nodig
Mise wisselt automatisch wanneer je de directory binnenkomt.
De Combo: chezmoi + mise
Dit is hoe ik ze combineer:
1. mise Config in chezmoi
chezmoi add ~/.config/mise/config.toml
Nu zijn mijn tool versies onderdeel van mijn dotfiles. Dezelfde versies overal.
2. Bootstrap Script
# ~/.local/share/chezmoi/run_once_before_10-install-mise.sh
#!/bin/bash
if ! command -v mise &> /dev/null; then
curl https://mise.run | sh
fi
# ~/.local/share/chezmoi/run_once_after_90-mise-install.sh
#!/bin/bash
# Draai nadat dotfiles op hun plek staan
mise install
De before en after in de bestandsnaam bepalen de uitvoervolgorde.
3. Shell Integratie
Mijn .zshrc.tmpl:
# mise
if command -v mise &> /dev/null; then
eval "$(mise activate zsh)"
fi
# ... rest van config ...
4. Machine-Specifieke Tools
Verschillende machines hebben verschillende tools nodig:
# ~/.config/mise/config.toml.tmpl
[tools]
go = "1.22"
node = "lts"
python = "3.12"
{{ if .work -}}
terraform = "1.7"
kubectl = "1.29"
helm = "3.14"
{{- end }}
{{ if eq .chezmoi.os "darwin" -}}
# macOS-specifieke tools
{{- end }}
Mijn Bootstrap Proces
Op een verse machine:
# 1. Installeer chezmoi en init vanuit repo
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply yourusername
# 2. Dat is het
Het init script:
- Kloont mijn dotfiles repo
- Vraagt om machine-specifieke data (work/personal, email)
- Ontsleutelt secrets (vraagt om age key)
- Draait bootstrap scripts (installeert mise, packages)
- Past alle dotfiles toe
Binnen 5 minuten heb ik een volledig geconfigureerde omgeving.
Tips en Tricks
Externe Bestanden
Bestanden nodig van URLs of git repos?
# ~/.local/share/chezmoi/.chezmoiexternal.toml
[".oh-my-zsh"]
type = "archive"
url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz"
exact = true
stripComponents = 1
refreshPeriod = "168h"
[".config/nvim/lazy-lock.json"]
type = "file"
url = "https://raw.githubusercontent.com/you/nvim-config/main/lazy-lock.json"
refreshPeriod = "24h"
Bestanden Negeren
# ~/.local/share/chezmoi/.chezmoiignore
# Sync deze nooit
.DS_Store
*.swp
.zsh_history
# Machine-specifieke ignores
{{ if not .work }}
.config/work-vpn/
{{ end }}
Re-add Na Bewerken
Als je een bestand direct bewerkt in plaats van via chezmoi edit:
# Re-add om source state te updaten
chezmoi re-add
Check voor Drift
Kijk of je home directory is afgeweken van chezmoi:
chezmoi verify
Conclusie
Na jaren zoeken — bare git repos, GNU Stow, Ansible playbooks, en een half dozijn andere tools — is chezmoi + mise de setup die eindelijk blijft plakken. Het handelt alles af:
- Cross-machine sync via git
- Machine-specifieke config via templates
- Secrets via age encryptie
- Tool versies via mise
- Bootstrap via scripts
De initiële setup kost wat tijd. Maar als het eenmaal staat, heb je een reproduceerbare development omgeving die je overal volgt.
Nieuwe machine? Eén commando. Nieuwe tool versie nodig? Update config.toml, push, pull op andere machines. Klaar.
Is het overkill voor iemand met één machine? Misschien. Maar als je regelmatig nieuwe machines opzet, VMs gebruikt, of gewoon de gemoedsrust wilt dat je config versioned en gebackupt is — probeer het eens.
chezmoi init --apply yourusername
Je toekomstige zelf zal je dankbaar zijn.
Resources:
- chezmoi.io — officiële documentatie
- mise.jdx.dev — mise documentatie
- age encryption — simpele, moderne encryptie
