Declarative, cross-platform machine setup for macOS and WSL/Ubuntu.
One command clones this repo, installs every tool, and renders every config file into place. The same source drives both operating systems; OS-specific behaviour is handled explicitly, not by accident.
# macOS or WSL/Ubuntu — fresh machine, one line:
sh -c "$(curl -fsSL https://chezmoi.io/get)" -- init --apply davethaiReplace
davethaiwith this repo's owner if you forked it. The command installschezmoi, clones the repo, prompts for your git identity + profile, then bootstraps the machine. See docs/tutorials/getting-started.md for the guided walkthrough.
chezmoi init asks for a profile:
full(default) — everything: k8s/devops stack, containers (Docker + podman), PHP build deps, and on macOS the GUI casks + Dock + system defaults.minimal— shell + prompt + core CLI only (starship, eza, bat, fzf, zoxide, rg, fd, jq, yq, gh, lazygit, delta, neovim, uv/node/python). For sandboxes / remote dev boxes where the heavy tooling is just noise.
Every layer owns exactly one responsibility, and lives in the tool that is idiomatic for that job — not whatever happens to be installed.
chezmoi places your files · mise installs your tools · Homebrew installs Mac apps · apt bootstraps Linux. Everything else is config that those four manage.
| Layer | Tool | Owns | Config | OS |
|---|---|---|---|---|
| Dotfile sync | chezmoi | Renders/syncs config into $HOME, templating, per-machine values |
this repo + home/.chezmoi.toml.tmpl |
both |
| CLI tools | mise | Installs & pins CLI tools + language runtimes (kubectl, k9s, helm, node, eza, starship…) | ~/.config/mise/config.toml |
both |
| GUI + store apps | Homebrew + mas | macOS GUI casks, Mac App Store, mac-native CLIs (ffmpeg, ollama…) | Brewfile |
macOS |
| System bootstrap | apt | Base libs + zsh on Linux | bootstrap script | WSL/Ubuntu |
| Shell | zsh + zinit | Interactive shell, plugins, aliases, keybinds | ~/.zshrc, ~/.config/zsh/* |
both |
| Prompt | starship | Prompt rendering | ~/.config/starship.toml |
both |
| Terminal | ghostty | Terminal emulator | ~/.config/ghostty/config |
macOS |
| macOS settings | defaults + dockutil | OS prefs (dark mode, Finder, Dock) + dock contents | home/.chezmoiscripts/* |
macOS |
| Git hooks | hk | Pre-commit secret scanning | hk.pkl |
this repo |
| Per-project tooling | mise + hk (in the project) | Per-repo tool versions & hooks — not this repo | each project's own mise.toml / hk.pkl |
per-project |
Config you tune by hand (mise, starship, hk) is schema-validated in your editor; see Type-safety.
These are identical on macOS and WSL/Ubuntu:
- CLI toolchain — declared once in
home/dot_config/mise/config.toml.miseinstalls kubectl, k9s, helm, terraform, node, python, rust, gh, starship, eza, bat, fzf, zoxide, and the rest. - Shell — zsh + zinit (pinned) with
autosuggestions, fast-syntax-highlighting, fzf-tab, completions.
Modular config in
home/dot_config/zsh/. - Prompt — starship (kubernetes / terraform / cloud aware).
- Git — identity, SSH commit signing, and a work
includeIf, all templated from your answers to thechezmoi initprompts. - Git hooks —
hk.pklrunstrufflehogon every commit.
Adds, on top of the shared layer:
- GUI apps + Mac App Store —
Brewfile(casks +mas), applied viabrew bundle. - System preferences —
run_onchange_after_40-macos-defaults: dark mode, 24-hour clock, Finder icon view, Dock autohide / no-recents, Bluetooth + battery-% in the menu bar. - Dock —
run_onchange_after_50-dockrebuilds the Dock from an explicit, ordered app list usingdockutil. - Terminal — Ghostty.
Some things stay imperative by necessity — see docs/how-to/manual-app-setup.md (Affinity suite, Snapply dictation, VS Code/Chrome sign-in sync, SSH keys).
Adds, on top of the shared layer:
- System bootstrap —
run_once_before_10-bootstrapinstallsbuild-essential zsh curl git file unzipviaapt. Everything else comes frommise, so there is no Brewfile and no Homebrew on Linux. - No GUI / Dock /
defaultssteps run (guarded with{{ if eq .chezmoi.os "darwin" }}checks).
See docs/tutorials/getting-started.md for the WSL distro import steps.
Project-level tooling lives in each project repo, never here:
mise.tomlpins that project's tool/runtime versions (overriding the global config in this repo).hk.pkldefines that project's own git hooks.
This repo is strictly machine/global config. The boundary is deliberate: cloning a project should configure the project; cloning this repo should configure the machine.
The config you edit by hand validates against a schema in-editor. Open the repo in VS Code and accept the recommended extensions.
| File | Validated by |
|---|---|
hk.pkl |
Pkl language server (real static types) |
mise/config.toml |
#:schema + Even Better TOML |
starship.toml |
$schema + Even Better TOML |
| shell scripts | shellcheck |
Untyped-by-nature files (Brewfile, ghostty/config, macOS defaults) are
validated by their own tools (brew bundle, ghostty +show-config, defaults).
Organised by the Diátaxis framework:
- Tutorial — getting started on a fresh machine
- How-to — add a package · add a dotfile · customize macOS / the Dock · manual app setup · PHP & Laravel
- Reference — repository layout · commands cheatsheet
- Explanation — architecture & design decisions
- Runbook — day-to-day operations & troubleshooting
@use-the-fork— first introduced me to declarative config.- chezmoi, mise, hk, starship.