Bootstrap a new machine
One-liner
DF_NAME="Your Name" DF_EMAIL="[email protected]" \
curl -fsSL https://raw.githubusercontent.com/cadebrown/dotfiles/main/bootstrap.sh | bash
Runs fully unattended. DF_NAME / DF_EMAIL are needed here because piping the
script into bash occupies stdin, leaving chezmoi no terminal to prompt on. The
values are cached in ~/.config/chezmoi/chezmoi.toml, so re-runs read from the
cache and need nothing.
Interactive (prompts for name + email)
To be prompted instead of pre-seeding, run from a local clone in a real terminal — chezmoi then has a TTY to read from:
git clone https://github.com/cadebrown/dotfiles ~/dotfiles
~/dotfiles/bootstrap.sh
Modes
bootstrap.sh # install (default) — full idempotent setup
bootstrap.sh update # git pull + chezmoi apply + refresh tools
bootstrap.sh upgrade # update + brew upgrade + cargo upgrade
update pulls the latest dotfiles, applies chezmoi, refreshes zsh plugins, and re-runs all install scripts (which skip already-installed tools). Skips scratch setup and repo cloning.
upgrade does everything update does, plus enables Homebrew upgrades (DF_BREW_UPGRADE=1) and forces cargo-binstall to re-check for newer binaries.
macOS
Requirements
| Requirement | How to get it |
|---|---|
| macOS 13+ (Ventura or later) | — |
| Xcode Command Line Tools | Homebrew prompts automatically, or: xcode-select --install |
| Internet access | — |
Sudo is required for the Homebrew installer.
What gets installed
Paths below use $LOCAL_PLAT, which is $HOME/.local by default and $HOME/.local/$PLAT when PLAT isolation is enabled. $ARCH_BIN is $LOCAL_PLAT/bin.
- chezmoi →
$ARCH_BIN/chezmoi - Dotfiles applied via
chezmoi apply- Shell configs for both zsh (
.zprofile) and bash (.bash_profile) - Both shells do identical PLAT capability detection and PATH setup
- Shell configs for both zsh (
- oh-my-zsh + plugins (pure prompt, autosuggestions, fast-syntax-highlighting, completions)
- Homebrew →
/opt/homebrew(Apple Silicon) or/usr/local(Intel)- All packages from
packages/Brewfile— CLI tools, casks, macOS-only apps - Includes
rustup(Homebrew’s code-signed build — required for macOS Sequoia+)
- All packages from
- Services: colima registered as a login service (rootless Docker)
- macOS defaults: Dock, Finder, keyboard, trackpad, screenshots, Safari, iTerm2 preferences
- Node.js via nvm →
$LOCAL_PLAT/nvm/ - Rust toolchain →
$LOCAL_PLAT/rustup/+$LOCAL_PLAT/cargo/- Homebrew’s
rustup(code-signed), required on macOS Sequoia+ where the linker enforcescom.apple.provenance cargo-binstalldownloads pre-built binaries from GitHub releases when available, falls back to source- Cargo tools install to
$LOCAL_PLAT/cargo/bin/
- Homebrew’s
- Python via uv →
$LOCAL_PLAT/uv/tools/<tool>/(one isolated venv per CLI tool), entrypoints in$ARCH_BIN - Claude Code native binary →
$ARCH_BIN/claude+ plugins + MCP servers + overlay skills - Codex CLI native binary →
$ARCH_BIN/codex, plus managed config + hooks under~/.codex/ - Cursor / VS Code — settings symlinked from
home/dot_cursor/; extensions installed frompackages/{cursor,vscode}-extensions.txt - CMake toolchain files →
$LOCAL_PLAT/cmake/toolchains/- Versioned files:
llvm-21.cmake,llvm-22.cmake,gcc-13.cmake,gcc-15.cmake, plus shared_brew.cmake ~/.profilesetsCMAKE_TOOLCHAIN_FILEto the highest installed LLVM toolchain automatically- Switch at runtime with the
tcshell function (e.g.tc gcc-15,tc llvm-22)
- Versioned files:
- Local LLM tooling — HuggingFace cache + binary checks
- Creates
$LOCAL_PLAT/.cache/huggingfacefor mlx-lm weights - Verifies ollama / mlx-lm / mlx-openai-server / opencode binaries
- Creates
- Agent memory stack — cass session-history search, ~/kb + qmd knowledge index, memory daemons
- Blender MCP addon — installs
addon.pyinto the active Blender profile and enables it - Auth (opt-in:
DF_DO_AUTH=1) — guided service-token setup; see Auth - Overlays — runs
bootstrap.shof anydotfiles-*/overlay alongside this repo; see Overlays
Total time: ~2 minutes on subsequent runs (idempotent, mostly bottle pours); ~5–10 minutes on a fresh machine.
Linux
Requirements
| Requirement | Notes |
|---|---|
| x86_64 or aarch64 | — |
git and curl | Pre-installed on most systems |
| Internet access | — |
No sudo required. No Docker or Podman needed.
What gets installed
Paths use $LOCAL_PLAT, which is $HOME/.local by default (or $HOME/.local/$PLAT with PLAT isolation enabled — recommended for shared NFS homes).
- chezmoi →
$ARCH_BIN/chezmoi($ARCH_BIN=$LOCAL_PLAT/bin) - Dotfiles applied via
chezmoi apply- Shell configs for both zsh (
.zprofile) and bash (.bash_profile)
- Shell configs for both zsh (
- oh-my-zsh + plugins
- Homebrew →
$LOCAL_PLAT/brew/(native install, no Docker/Podman needed)- Installs Homebrew’s own glibc 2.35 first — binaries are fully self-contained
- Most packages pour as precompiled bottles; glibc builds from source (~2 min) on first run
- Custom [email protected] patches applied automatically for Linux compatibility
- Node.js via nvm →
$LOCAL_PLAT/nvm/ - Rust via
sh.rustup.rs→$LOCAL_PLAT/rustup/+$LOCAL_PLAT/cargo/cargo-binstalldownloads pre-built binaries from GitHub releases when available, falls back to source
- Python via uv →
$LOCAL_PLAT/uv/tools/<tool>/(per-CLI-tool venvs), entrypoints in$ARCH_BIN - Claude Code native binary →
$ARCH_BIN/claude+ plugins + MCP servers - Codex CLI native binary →
$ARCH_BIN/codex - Cursor / VS Code — extensions from
packages/{cursor,vscode}-extensions.txt - CMake toolchain files →
$LOCAL_PLAT/cmake/toolchains/(llvm-21/22.cmake,gcc-13/15.cmake,_brew.cmake)~/.profileauto-setsCMAKE_TOOLCHAIN_FILEto the highest installed LLVM toolchain- Switch with the
tcshell function (e.g.tc gcc-15,tc llvm-22)
- Local LLM tooling — HuggingFace cache + ollama/mlx-lm/mlx-openai-server/opencode binary checks
- Agent memory stack — cass session-history search, ~/kb + qmd knowledge index (daemons lazy-start from shell profiles)
- Auth (opt-in:
DF_DO_AUTH=1) — guided token setup; see Auth - Overlays — runs
bootstrap.shof anydotfiles-*/overlay; see Overlays
Total time: ~5 minutes on a fast connection.
Skipping steps
Any step can be disabled with an environment variable:
DF_DO_SCRATCH=0 # skip scratch space symlink setup
DF_DO_DIRS=0 # skip home directory creation (~/dev, ~/bones, ~/misc)
DF_DO_PACKAGES=0 # skip Homebrew + brew bundle
DF_DO_MACOS_SERVICES=0 # skip colima service setup (macOS)
DF_DO_MACOS_SETTINGS=0 # skip macOS settings (Dock, Finder, keyboard, etc.)
DF_DO_MACOS_QUICK_ACTIONS=0 # skip Finder Quick Actions install (macOS)
DF_DO_ZSH=0 # skip oh-my-zsh
DF_DO_NODE=0 # skip nvm + Node.js + global npm packages
DF_DO_RUST=0 # skip rustup + cargo tools
DF_DO_PYTHON=0 # skip uv + per-tool venvs
DF_DO_CLAUDE=0 # skip Claude Code install + plugins + MCP servers
DF_DO_CODEX=0 # skip Codex CLI install
DF_DO_CURSOR=0 # skip Cursor settings symlinks + extension install
DF_DO_VSCODE=0 # skip VS Code extension install
DF_DO_CMAKE=0 # skip CMake toolchain file deployment
DF_DO_LOCAL_LLM=0 # skip local LLM setup (HuggingFace cache + binary checks)
DF_DO_MEMORY=0 # skip the agent memory stack (cass + qmd + ~/kb)
DF_DO_BLENDER_MCP=0 # skip Blender MCP addon install
DF_DO_AUTH=1 # run interactive API token setup (default 0)
DF_DO_OVERLAYS=0 # skip all overlay bootstraps (dotfiles-*/bootstrap.sh)
DF_USE_PLAT=1 # opt in to per-PLAT directory isolation (default 0; flat layout)
DF_BREW_UPGRADE=0 # skip Homebrew upgrades (macOS default: 1, Linux default: 0)
The complete reference lives at Env vars.
Example — dotfiles only, no runtimes:
DF_DO_PACKAGES=0 DF_DO_ZSH=0 DF_DO_NODE=0 \
DF_DO_RUST=0 DF_DO_PYTHON=0 DF_DO_CLAUDE=0 \
~/dotfiles/bootstrap.sh
Debug mode
For verbose output with command timing:
DF_DEBUG=1 ~/dotfiles/bootstrap.sh
Shows [dbug] lines for every command executed by run_logged, including exit codes and elapsed time.
Shared home directories (NFS/GPFS)
If you share $HOME across multiple machines with different CPU architectures, enable PLAT isolation:
DF_USE_PLAT=1 ~/dotfiles/bootstrap.sh
(Or persist it in chezmoi data: chezmoi edit ~/.config/chezmoi/chezmoi.toml and set use_plat = true.)
With PLAT on, each machine installs compiled tools to its own ~/.local/$PLAT/ directory:
| Machine | PLAT | Where tools live |
|---|---|---|
| AVX-512 Linux (e.g. Ice Lake) | plat_Linux_x86-64-v4 | ~/.local/plat_Linux_x86-64-v4/ |
| AVX2 Linux (e.g. Haswell/Zen2) | plat_Linux_x86-64-v3 | ~/.local/plat_Linux_x86-64-v3/ |
| ARM Linux | plat_Linux_aarch64 | ~/.local/plat_Linux_aarch64/ |
| Apple Silicon | plat_Darwin_arm64 | ~/.local/plat_Darwin_arm64/ |
Text configs (dotfiles) are arch-neutral and shared freely across all machines. See PLAT isolation for the deeper explanation, the decommission script, and the failure modes that PLAT exists to prevent.
Scratch space (large quota environments)
If your home directory has a small quota (common on HPC NFS mounts), direct large directories to local scratch storage:
DF_SCRATCH=/scratch/$USER \
DF_NAME="Your Name" DF_EMAIL="[email protected]" \
~/dotfiles/bootstrap.sh
This symlinks large directories to $DF_SCRATCH/.paths/ before any tools are installed, so the multi-GB Homebrew prefix and caches never touch NFS.
Default directories redirected to scratch (controlled by DF_LINKS):
~/.local— PLAT directories, Homebrew prefix, tool binaries~/.cache— ccache, sccache, pip/uv cache~/.vscode/~/.vscode-server— VS Code extensions and data~/.cursor/~/.cursor-server— Cursor IDE data~/.nv— NVIDIA shader and OptiX cache~/.npm— npm cache~/.claude— Claude Code data and cache~/.oh-my-zsh/~/.oh-my-zsh-custom— oh-my-zsh and plugins
Auth (API tokens)
See the dedicated Auth page for the full walkthrough. Quick reference:
bash ~/dotfiles/install/auth.sh # walk every service interactively
bash ~/dotfiles/install/auth.sh status # show current state, no prompts
bash ~/dotfiles/install/auth.sh huggingface # set/update one service
bash ~/dotfiles/install/auth.sh gh # `gh auth login` (browser flow)
# Or during bootstrap:
DF_DO_AUTH=1 ~/dotfiles/bootstrap.sh
Covers GitHub, Anthropic, OpenAI, Cloudflare, HuggingFace, plus a separate gh auth login flow for the Claude GitHub MCP. Tokens land in ~/.<service>.env files (chmod 600) and are auto-sourced by install scripts and login shells. Each prompt shows a skip if: hint — most users only set 1–2 of them.