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/ollama/mlxserve auto-start is opt-in (
DF_START_LOCAL_SERVICES=1); off by default. At the default, colima/ollama are simply left alone, but mlxserve is stopped andlaunchctl disabled — launchd re-loads its plist at every login otherwise, so a hand-started mlxserve will not survive a bootstrap run. Docker CLI plugins are always linked. - macOS defaults: Dock, Finder, keyboard, trackpad, screenshots, Safari, iTerm2 preferences
- Python via uv →
$LOCAL_PLAT/uv/tools/<tool>/(one isolated venv per CLI tool), entrypoints in$ARCH_BIN;DF_PROFILE=coreskipspip-full.txt - Node.js 24 LTS via pinned nvm →
$LOCAL_PLAT/nvm/- Uses uv’s Python for
node-gypfallbacks when an npm package has no prebuilt binary
- Uses uv’s Python for
- Rust toolchain →
$LOCAL_PLAT/rustup/+$LOCAL_PLAT/cargo/- Homebrew’s
rustup(code-signed), required on macOS Sequoia+ where the linker enforcescom.apple.provenance DF_PROFILE=corekeeps rustup and rust-analyzer but skips optionalcargo.txttoolscargo-binstalldownloads pre-built binaries from GitHub releases when available, falls back to source- Cargo tools install to
$LOCAL_PLAT/cargo/bin/
- Homebrew’s
- Go CLI tools from
packages/go.txt→$ARCH_BIN(Go itself comes from the Brewfile) - Lean 4 via elan →
$LOCAL_PLAT/elan/- Toolchains are arch-specific (~1.5 GB each), so
ELAN_HOMEis PLAT-isolated like rustup - Installs and defaults a pinned toolchain; projects override via their own
lean-toolchainfile
- Toolchains are arch-specific (~1.5 GB each), so
- TeX — MacTeX comes from the Brewfile cask; this step verifies it and puts
/Library/TeX/texbinon PATH - 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 archive, ~/kb + qmd knowledge index; cass indexing stays manual
- Agent skills — installs
packages/agent-skills.txtinto the shared~/.claude/skillstree - 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, curl, and python3 | Pre-installed on most systems; Python runs the Homebrew formula patch layer before uv is installed |
| 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
- Python via uv →
$LOCAL_PLAT/uv/tools/<tool>/(per-CLI-tool venvs), entrypoints in$ARCH_BIN - Node.js via nvm →
$LOCAL_PLAT/nvm/- Uses uv’s Python for
node-gypfallbacks when an npm package has no prebuilt binary
- Uses uv’s Python for
- 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
- Go CLI tools from
packages/go.txt→$ARCH_BIN - Lean 4 via elan →
$LOCAL_PLAT/elan/(pinned default toolchain; projects override vialean-toolchain) - Julia via Juliaup, with release channels and depots under
$LOCAL_PLAT/julia/ - TeX via TinyTeX →
$LOCAL_PLAT/tex/.TinyTeX, withtlmgrsys_binpointed at$ARCH_BIN- ~200 MB base instead of multi-GB; missing packages install on demand with
tlmgr install <pkg>
- ~200 MB base instead of multi-GB; missing packages install on demand with
- Quarto via the macOS cask or a checksum-verified rootless Linux archive
- 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 archive, ~/kb + qmd knowledge index; cass indexing stays manual
- Agent skills — installs
packages/agent-skills.txtinto the shared~/.claude/skillstree - 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_GO=0 # skip Go CLI tools from go.txt
DF_DO_JULIA=0 # skip Julia release-channel management
DF_DO_LEAN=0 # skip the Lean 4 toolchain (elan + pinned toolchain)
DF_DO_LATEX=0 # skip the TeX distribution (MacTeX verify / TinyTeX)
DF_DO_QUARTO=0 # skip Quarto verification/install
DF_DO_CLAUDE=0 # skip Claude Code install + plugins + MCP servers
DF_DO_CODEX=0 # skip Codex CLI install
DF_DO_CLAUDE_DESKTOP=0 # skip Claude Desktop tracked preferences (macOS)
DF_DO_CODEX_DESKTOP=0 # skip Codex desktop app tracked preferences (macOS)
DF_DO_LINEARMOUSE=0 # skip LinearMouse tracked settings (macOS)
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_SKILLS=0 # skip agent skills from agent-skills.txt
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 (default except in upgrade mode)
DF_STRICT_UPGRADE=0 # report stale tools without failing upgrade
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~/.oh-my-zsh/~/.oh-my-zsh-custom— oh-my-zsh and plugins
Plus the heavy unmanaged entries of the two agent config dirs, which stay real directories themselves because chezmoi manages files inside them:
~/.claude(controlled byDF_CLAUDE_LINKS):projects(history + memory),plugins,file-history~/.codex(controlled byDF_CODEX_LINKS):sessions(transcripts — usually the largest single directory on the machine),cache,plugins,attachments,shell_snapshots,.tmp,tmp, plus the loose*.sqlitedatabases
~/.codex is skipped while any process holds a file there open — see Scratch space.
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.