Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overlays

An overlay is a separate repo (typically private) that extends this base dotfiles without forking. Overlays live next to the base in $DF_ROOT/dotfiles-*/ and get discovered automatically — their package lists, install scripts, claude skills, and Codex skills compose with the base.

Use overlays for:

  • Personal/private content that shouldn’t ship in the public repo (dotfiles-personal/)
  • Org-specific setup (dotfiles-acme/, dotfiles-lab/)
  • Hardware-specific extras (dotfiles-nvidia/ for CUDA toolkits, MCPs, kernels)

Discovery model

The base _lib.sh defines DF_OVERLAYS (an array of paths to overlay roots) and overlay_package_files() (a helper that returns base-first-then-overlays paths for any package list filename).

   ~/dotfiles/                        ← base, public
   └── packages/mcp-servers.txt       (5 entries: cloudflare, github, openaiDeveloperDocs, context7, blender)

   ~/dotfiles-nvidia/                 ← overlay, private
   └── packages/mcp-servers.txt       (NVIDIA-internal MaaS entries)
                       │
                       │  install/claude.sh + install/codex.sh:
                       │    while IFS= read -r f; do
                       │        _register_mcps_from "$f"   # claude.sh
                       │        _emit_mcp_blocks_to ...    # codex.sh
                       │    done < <(overlay_package_files "mcp-servers.txt")
                       │
                       ▼
   Effective merged list (base first, then each overlay sorted) — same list
   consumed by both Claude (`claude mcp add`) and Codex (`[mcp_servers.*]`).

The merge is append-only — overlays add to the base, they don’t replace it. Order is base, then overlays in lexicographic path order.

What an overlay can provide

Path in overlayEffect
packages/cargo.txtadditional Rust crates installed by install/rust.sh
packages/mcp-servers.txtadditional MCP servers registered by install/claude.sh and install/codex.sh
packages/claude-plugins.txtadditional Claude plugins installed
packages/<other>.txtdiscovered via overlay_package_files() — pattern works for any list-style file
home/dot_claude/CLAUDE.mdappended to ~/.claude/CLAUDE.md via the chezmoi template
home/dot_claude/skills/<name>/SKILL.mddeployed to ~/.claude/skills/<name>/ by install/claude.sh
install/auth.shruns alongside the base auth walk during step 7.5 (post-base auth)
install/<other>.shsource _lib.sh and use the same conventions; invoked from the overlay’s bootstrap
bootstrap.shruns as the base bootstrap step 8 (after everything else)

The base intentionally has no built-in awareness of any specific overlay — discovery is purely by directory glob (dotfiles-*/).

Creating an overlay

# 1. Create the repo somewhere accessible (or just a local dir):
mkdir -p ~/dotfiles-mine
cd ~/dotfiles-mine
git init

# 2. Add a package file or two:
mkdir -p packages
cat > packages/cargo.txt <<'EOF'
# my private cargo additions
hyperfine
flamegraph
EOF

# 3. Optionally, a bootstrap to do per-overlay setup:
cat > bootstrap.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
source "$DF_ROOT/install/_lib.sh"   # base helpers (log_info, etc.)
log_section "dotfiles-mine"
# ... your custom logic ...
EOF
chmod +x bootstrap.sh

# 4. Symlink (or clone) it next to the base:
ln -s ~/dotfiles-mine ~/dotfiles/dotfiles-mine

# 5. Re-run the base bootstrap. Step 8 picks up your overlay automatically.
~/dotfiles/bootstrap.sh

The directory name must start with dotfiles- for the glob to find it. Common names: dotfiles-personal, dotfiles-work, dotfiles-{laptop,desktop,server}, dotfiles-{nvidia,amd,intel}.

chezmoi integration

Overlays don’t usually own their own chezmoi root — instead, the base home/ template references overlay files via glob:

{{ glob (joinPath .chezmoi.workingTree "dotfiles-*/packages/mcp-servers.txt") }}

This pattern is used by home/run_onchange_*.sh.tmpl scripts, so chezmoi notices when any overlay’s package file changes (not just the base) and re-fires the install script.

For chezmoi-managed content (skills, claude/codex configs), the base’s chezmoi templates have {{ if (stat ...) }} guards that pull the overlay file’s contents in if present.

Why overlays vs forks

A fork makes you carry every base change into your private tree forever. An overlay lets you git pull the base independently and keep your private stuff strictly additive. Conflicts only happen if the base removes something your overlay depended on (rare; the discovery contract is stable).

For one-off per-machine tweaks that aren’t worth a whole overlay, see Managing dotfiles → Customizing per-machine. Overlays are the right answer when the tweak is a coherent set of files you’d commit together.