Réinstaller une machine, c'est souvent une journée perdue. Retrouver le bon alias, le plugin zsh oublié, la font qui ne s'affiche pas, le.gitconfig oublié...
Lorsqu'on utilise plusieurs systèmes. Dans mon cas Manjaro et macOS en perso, Ubuntu en pro. Il devient vite nécessaire de traiter sa configuration comme du code: versionnée, reproductible. Le résultat: une seule commande pour retrouver un environnement complet.
chezmoi init --apply <user>
Dans cet article: l'architecture de mes dotfiles, les choix techniques, et comment reproduire cette approche.
Le problème
Les dotfiles, ce sont ces dizaines de fichiers cachés qui définissent un environnement de travail : .zshrc, .gitconfig, .ssh/config, les settings VS Code...
Pas de versioning. On modifie un alias, ça casse quelque chose, impossible de revenir en arrière.
Pas de portabilité. Une config Arch ne marche pas sur Ubuntu. On maintient mentalement les différences entre ses machines.
Secrets éparpillés. Clés SSH, tokens npm, configs privées. Certains finissent copiés à la main, d'autres sont perdus.
Setup manuel. Pour chaque nouvelle machine, c'est interminable. Il manque toujours un outil, une config, un réglage.
Pendant longtemps, j'avais un repo git avec des fichiers plus ou moins organisés que je restaurais manuellement sur chaque nouveau système, en espérant qu'ils soient à jour. Ça fonctionnait, mais c'était fragile, source d'erreurs, et il manquait souvent des éléments importants au moment de la réinstallation.
Pourquoi chezmoi ?
Parmi les outils disponibles pour gérer ses dotfiles (stow, yadm, etc.), j'ai choisi chezmoi. Sa philosophie est simple: maintenir un dépôt git comme source de vérité, puis générer automatiquement les fichiers dans le $HOME. Contrairement aux approches basées uniquement sur des symlinks, chezmoi apporte ce qu'il faut quand on travaille sur plusieurs machines: templating par OS ou par hôte, gestion des secrets, scripts d'initialisation et application idempotente de la configuration.
Des solutions plus poussées comme Nix existent, mais la configuration système n'est clairement pas un sujet sur lequel je souhaite passer trop de temps. L'objectif est simple: gagner du temps et du confort. Il me faut un système suffisamment puissant pour gérer des environnements hétérogènes, tout en restant simple à initialiser sur une nouvelle machine.
J'ai utilisé principalement 3 fonctionnalités de chezmoi:
Le templating : un seul fichier source peut générer des configs différentes selon l'OS, le rôle de la machine ou des préférences personnelles. Le .zshrc, la config git, les settings VS Code sont tous templatés.
Le chiffrement natif : chezmoi intègre age pour chiffrer les fichiers sensibles directement dans le repo. Le .npmrc avec ses tokens est versionné, mais chiffré.
Les scripts run_onchange_ : des scripts qui se ré-exécutent automatiquement quand leur contenu (ou celui d'un fichier tracké) change. Pas besoin de se souvenir de ce qu'il faut relancer.
La boîte à outils
Avant de parler architecture, voici les outils que j'ai retenus. Le critère principal: des outils modernes, rapides, et qui remplacent avantageusement les commandes classiques.
Shell
J'ai longtemps utilisé oh-my-zsh, qui est une excellente solution. Mais dorénavant, je préféré passer à du zsh natif pour un système plus simple et plus performant. Pas de framework, pas de plugin manager. Juste des modules sourcés depuis un .zshrc minimal :
# ~/.zshrc - managed by chezmoi
source ~/.config/zsh/path.zsh
source ~/.config/zsh/exports.zsh
source ~/.config/zsh/completion.zsh
source ~/.config/zsh/aliases.zsh
source ~/.config/zsh/functions.zsh
source ~/.config/zsh/integrations.zshChaque fichier a une responsabilité claire. Besoin de modifier un alias ? C'est dans aliases.zsh. Un problème de PATH ? C'est dans path.zsh.
Système
Les classiques ls, cat, find, du ou ps font le job, mais leurs alternatives modernes offrent une meilleure ergonomie: coloration, sortie lisible, performances.
Outil | Remplace | Pourquoi |
| Coloration, icônes, arborescence intégrée ( | |
| Coloration syntaxique, numéros de ligne, intégration git | |
| Syntaxe simple, rapide, respecte | |
| Recherche récursive ultra-rapide, respecte | |
— | Recherche floue interactive pour fichiers, historique, branches... | |
| Visualisation de l'espace disque en arborescence | |
| Liste des processus lisible, colorée, avec filtrage |
Développement
Outil | Rôle |
Gestionnaire de runtimes (Node, Python, Bun). Remplace nvm/pyenv/asdf en un seul outil | |
Charge automatiquement des variables d'environnement par projet via un | |
Task runner. Comme | |
Interface TUI pour git. Staging, rebase, stash en quelques touches | |
CLI GitHub officielle. PRs, issues, actions depuis le terminal |
Interface
Outil | Rôle |
Terminal GPU-accelerated, rapide, configurable. Mon terminal principal | |
Prompt shell personnalisable écrit en Rust. Affiche contextuellemnt la branche git, la version du runtime, le status de la dernière commande. Compatible zsh, bash, fish. Très rapide |
Ces outils sont installés automatiquement par les scripts de bootstrap, selon les feature flags de la machine.
Architecture du dépôt
Voici la structure de mon dépôt dotfiles :
~/.local/share/chezmoi/
├── .chezmoidata.yaml # Variables & feature toggles
├── .chezmoi.toml.tmpl # Config chezmoi (encryption, données promptées)
├── run_onchange_bootstrap-system.sh.tmpl # Packages de base, CLI modernes, fonts
├── run_onchange_bootstrap-dev.sh.tmpl # mise, direnv, lazygit, docker
├── run_onchange_bootstrap-desktop.sh.tmpl # Ghostty, VS Code, Starship
├── dot_zshrc.tmpl # Point d'entrée zsh (modulaire)
├── dot_config/
│ ├── zsh/ # Modules : aliases, path, completion...
│ ├── git/
│ │ └── config.tmpl # Config git (XDG)
│ ├── ghostty/
│ │ └── config.tmpl # Terminal Ghostty
│ ├── starship.toml # Prompt Starship
│ └── mise/
│ └── config.toml.tmpl # Runtimes (Node, Python, Bun)
└── dot_local/
└── bin/
├── executable_dotfiles-doctor.sh.tmpl # Health check
└── executable_dotfiles-backup.sh.tmpl # Backup chiffréChaque fichier suit les conventions de nommage de chezmoi. Les préfixes et suffixes du nom source déterminent le comportement dans le $HOME :
Préfixe / Suffixe | Effet |
| Fichier caché ( |
| Fichier interprété comme template Go |
| Ajoute les bits d'exécution sur la cible |
| Retire toutes les permissions groupe et world (équivalent |
| Retire les bits d'écriture sur la cible |
| Fichier source chiffré (déchiffré à l'apply) |
| Script ré-exécuté quand son contenu change |
| Script exécuté une seule fois (jamais rejoué) |
| Répertoire strict : supprime les entrées absentes de la source |
| Script qui reçoit le fichier existant en stdin et produit la cible en stdout |
| Fichier créé uniquement s'il n'existe pas déjà |
| Supprime l'entrée correspondante dans le |
Les préfixes se combinent : private_dot_gitconfig.tmpl produit un .gitconfig templaté en 0600.
Feature toggles : un setup modulaire
Toutes les machines n'ont pas les mêmes besoins. Un serveur n'a pas besoin de Ghostty. Une machine perso n'a pas besoin des outils de dev. La solution : un fichier .chezmoidata.yaml qui pilote tout.
machine:
role: "desktop" # desktop ou server
desktop: "kde" # gnome, kde, sway, none
features:
gui: true
docker: true
kubernetes: false
cloud_sdk: false
work_profile: false
development:
editor: "code"
terminal: "ghostty"
tools:
node_version: "lts"
python_version: "3.11"Les scripts de bootstrap lisent ces flags :
{{ if not .features.install_dev_tools -}}
echo "Dev tools install disabled, skipping."
exit 0
{{ end -}}Besoin de Docker sur une machine ? Il suffit de passer docker: true, puis chezmoi apply. Le script se ré-exécute car le hash de .chezmoidata.yaml a changé. Le même repo, adapté à chaque contexte.
Gestion des secrets avec age
Les clés SSH privées ne sont jamais dans le repo (.chezmoiignore). Elles sont restaurées via un script de backup dédié, présenté plus bas. Le fichier .npmrc avec ses tokens est chiffré avec age, versionné dans git mais illisible sans la clé.
La clé age elle-même vit à ~/.config/sops/age/chezmoi.txt, protégée par le .gitignore. Pour une nouvelle machine, il suffit de la restaurer depuis un backup.
Bootstrap en 3 phases
L'installation se déroule dans un ordre précis :
Phase 1 / System : packages de base, outils CLI modernes (eza, bat, fd, ripgrep, fzf, dust, procs), fonts Nerd Font, zsh comme shell par défaut.
Phase 2 / Dev : mise pour les runtimes (Node, Python, Bun), direnv pour les variables d'environnement par projet, lazygit, gh, just, Docker.
Phase 3 / Desktop : Ghostty, VS Code, Starship, Chrome, Discord, Obsidian, Spotify.
Chaque phase est conditionnée par ses feature flags. Un serveur headless s'arrête à la phase 2 (voire 1). Le support multi-OS (Arch/Manjaro, Ubuntu, macOS) est géré par des blocs conditionnels dans les templates. Pas de magie ici, c'est une partie fastidieuse à maintenir :
{{ if or (eq $osId "arch") (eq $osId "manjaro") -}}
sudo pacman -S --needed --noconfirm mise direnv lazygit
{{ else if eq $osId "ubuntu" -}}
curl https://mise.run | sh
sudo apt install -y direnv
{{ else if eq $os "darwin" -}}
brew install mise direnv lazygit
{{ end -}}Outils de sécurité : doctor et backup
Deux scripts utilitaires complètent le setup :
dotfiles-doctor, un health check qui vérifie que tout est en place : zsh comme shell par défaut, starship actif, ghostty installé, mise configuré, docker disponible. Chaque vérification renvoie [OK], [WARN] ou [FAIL].
dotfiles-doctor.sh
# [OK] zsh is default shell
# [OK] starship prompt active
# [OK] ghostty installed
# [FAIL] direnv not founddotfiles-backup crée une archive chiffrée contenant les clés SSH, les clés GPG, la liste des extensions VS Code, les packages installés et les repos du workspace. Indispensable avant un changement de machine.
Résultat
Sur une machine neuve, tout se résume à :
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply kepennar
En quelques minutes :
Tous les paquets sont installés
Le shell est configuré (zsh + compinit + starship)
Git, VSCode,Obsidian sont prêts à l'emploi
Les secrets sont déchiffrés et en place
Le dépôt complet est disponible sur GitHub :