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 .gitconfig bevat tokens, mijn .ssh/config bevat 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 verandert
  • run_ — 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:

  1. Kloont mijn dotfiles repo
  2. Vraagt om machine-specifieke data (work/personal, email)
  3. Ontsleutelt secrets (vraagt om age key)
  4. Draait bootstrap scripts (installeert mise, packages)
  5. 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: