This repository is a clean, layered chezmoi setup for a primary personal dotfiles repo with two optional overlays:
work=trueenables a separate private work repo mounted at~/.config/workdevcontainer=truetrims host-specific behavior for container environmentspersonal=trueis the default profile for normal non-work, non-devcontainer machines
The base repo stays small and readable. Most variation lives in a few entrypoint templates instead of duplicated files.
Replace these placeholders in your chezmoi data:
YOUR_NAMEYOUR_PERSONAL_EMAILYOUR_WORK_EMAILYOUR_SIGNING_KEYYOUR_WORK_REPO_URLYOUR_WORK_HOST_ALIAS
Optional overrides:
editordefaults tocode --wait -non host machinesdevcontainerEditordefaults tovimin devcontainers
This repo uses these data keys:
nameemailworkEmailsigningKeyeditordevcontainerEditorworkRepoUrlworkHostAliaspersonalworkdevcontainer
Important: use --override-data or --override-data-file for arbitrary template data. If you have seen --data in examples, that is not the same thing.
Using a small TOML data file is the cleanest option.
Create a file like personal.toml:
name = "YOUR_NAME"
email = "YOUR_PERSONAL_EMAIL"
signingKey = "YOUR_SIGNING_KEY"
personal = true
work = false
devcontainer = falseThen initialize:
chezmoi init --apply --override-data-file personal.toml <YOUR_DOTFILES_REPO_URL>Create a file like work.toml:
name = "YOUR_NAME"
email = "YOUR_PERSONAL_EMAIL"
workEmail = "YOUR_WORK_EMAIL"
signingKey = "YOUR_SIGNING_KEY"
workRepoUrl = "YOUR_WORK_REPO_URL"
workHostAlias = "YOUR_WORK_HOST_ALIAS"
personal = false
work = true
devcontainer = falseThen initialize:
chezmoi init --apply --override-data-file work.toml <YOUR_DOTFILES_REPO_URL>This applies the base dotfiles and pulls the private work repo into ~/.config/work via .chezmoiexternal.toml.tmpl.
Create a file like devcontainer.toml:
name = "YOUR_NAME"
email = "YOUR_PERSONAL_EMAIL"
personal = false
work = false
devcontainer = trueThen initialize:
chezmoi init --apply --override-data-file devcontainer.toml <YOUR_DOTFILES_REPO_URL>In devcontainers, this setup intentionally skips:
- Homebrew bootstrap
- host Git config management
- macOS host CLI installation via Homebrew
- OrbStack shell init
- zoxide init
- the host 1Password SSH socket export
Devcontainers instead get a dedicated apt-based bootstrap script that installs the shared CLI subset from .chezmoidata/packages.yaml, including bat, direnv, fd, fzf, eza, mise, ripgrep, starship, zoxide, and 1password-cli. The repo intentionally does not manage ~/.gitconfig there because devcontainer tooling commonly mounts the host Git config.
Starship, direnv, fzf, and the rest of the shell layout still work as long as those tools are present in the container image.
- preview changes with
chezmoi diff - apply changes with
chezmoi apply - inspect status with
chezmoi status - refresh externals and apply updates with
chezmoi update - inspect rendered file contents with
chezmoi cat ~/.zshrc - inspect package metadata with
chezmoi data | rg packages
.
|-- .chezmoitemplates
| |-- brew-shellenv.sh
| `-- devcontainer-apt-bootstrap.sh
|-- .chezmoidata
| `-- packages.yaml
|-- .chezmoiignore.tmpl
|-- .chezmoi.toml.tmpl
|-- .chezmoiexternal.toml.tmpl
|-- README.md
|-- dot_gitconfig.tmpl
|-- dot_zprofile.tmpl
|-- dot_zshrc.tmpl
|-- dot_config
| |-- mise
| | `-- config.toml
| |-- starship.toml
| `-- zsh
| |-- aliases.zsh
| |-- completion.zsh
| |-- functions.zsh
| |-- history.zsh
| |-- options.zsh
| `-- tools.zsh.tmpl
|-- private_dot_ssh
| `-- private_config.tmpl
|-- run_once_before_10-install-homebrew.sh.tmpl
|-- run_once_before_20-install-devcontainer-cli-tools.sh.tmpl
|-- run_once_before_20-install-macos-cli-tools.sh.tmpl
`-- run_once_before_30-install-macos-apps.sh.tmpl
.chezmoi.toml.tmpldefines the data model and default profile flags.chezmoitemplates/brew-shellenv.shholds the shared Homebrew bootstrap used by the macOS install scripts.chezmoitemplates/devcontainer-apt-bootstrap.shholds the shared apt/sudo bootstrap used by the devcontainer install script.chezmoidata/packages.yamlis the single source of truth for installable tools and their package-manager mappings.chezmoiignore.tmplskips~/.gitconfigentirely in devcontainers.chezmoiexternal.toml.tmploptionally pulls a separate private work repo into~/.config/work.zprofilehandles environment and session setup.zshrcstays small and mostly sources files from~/.config/zsh~/.config/zsh/*.zshholds interactive shell behavior split by concern~/.config/mise/config.tomlkeeps mise config in the XDG location~/.ssh/configis private and sets up the 1Password SSH agentrun_once_before_*scripts handle host bootstrap steps before files are applied
The external work repo is intentionally separate from this base repo. A minimal work repo might provide files like:
~/.config/work/
|-- git/config
|-- ssh/config
`-- zsh/
|-- init.zsh
`-- zprofile.zsh
Only the base repo knows that ~/.config/work exists. The work repo owns everything inside it.
run_once_before_10-install-homebrew.sh.tmplinstalls Homebrew on macOS hosts if it is missingrun_once_before_20-install-macos-cli-tools.sh.tmplinstalls macOS host CLI tools from.chezmoidata/packages.yaml, includingmise,starship,eza,zoxide, andgit-lfsrun_once_before_20-install-devcontainer-cli-tools.sh.tmplinstalls the apt-based devcontainer CLI subset from.chezmoidata/packages.yaml, including1password-cli,eza,mise, andstarshiprun_once_before_30-install-macos-apps.sh.tmplinstalls the1passwordandorbstackmacOS apps via Homebrew casks on host machines only- the 1Password desktop app sign-in and agent setup are still your responsibility after install
The ordering keeps Homebrew installation, macOS CLI packages, devcontainer CLI packages, and macOS apps separate, while the tool list stays centralized in .chezmoidata/packages.yaml.
Bootstrap package lists live in .chezmoidata/packages.yaml so each tool is declared once.
Each package entry can define:
profilesto declare where a package applies, likehostanddevcontainerbrewonly when the Homebrew formula name differs from the package keyaptonly when the apt package name differs from the package keybrewCask = truefor macOS app installs where the cask name matches the package keyaptRepofor packages that need extra apt repository setup
This keeps the host and devcontainer scripts from drifting as the tool list changes.
- macOS is the main host platform
- zsh is your shell
code --wait -nis the default host editor andvimis the default devcontainer editor unless you override them in chezmoi data- the work repo, if used, is a normal Git repo reachable from the target machine
- devcontainers are assumed to be apt-based if you want chezmoi to install the managed CLI subset automatically
- devcontainers are also assumed to allow outbound network access for the
miseandstarshipinstaller scripts - OrbStack shell integration is loaded only if
~/.orbstack/shell/init.zshexists
- The base repo does not try to infer machine type from hostnames; profile flags are explicit by design
- Most files are plain files, not templates; templates are only used where profile-specific variation is actually needed
- Missing
~/.config/work/*files are safe: the base setup only sources them when the relevant profile is enabled, and Git tolerates a missing included config file ~/.ssh/configstarts by including~/.orbstack/ssh/configand~/.ssh/1Password/config, matching your current setup- On apt-based devcontainers,
fd-findandbatmay be exposed asfdfindandbatcat; the shell config and bootstrap handle those names so the zsh config still works cleanly