This is the payoff. Everything you've built across lessons 1–9 — templates, scripts, externals, secrets — gets deployed to a brand-new machine with a single command. No manual setup. No forgotten steps. One line and you're home.
On a fresh machine with nothing installed, run:
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply YOUR_GITHUB_USERNAME
That's it. This single command:
github.com/YOUR_GITHUB_USERNAME/dotfilesrun_once_ and run_onchange_ scriptsIf your repo is named something other than dotfiles, pass the full URL instead:
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply --source=https://github.com/YOUR_USERNAME/my-dotfiles.git
The Prompt step is powered by your .chezmoi.toml.tmpl file. This template generates the chezmoi config file on first run, asking the user (you) for values that templates depend on — like which machine type this is.
When chezmoi runs init and finds a file named .chezmoi.toml.tmpl in your source state root, it executes that template to generate ~/.config/chezmoi/chezmoi.toml. This is where you prompt for machine-specific values.
Here's how it works with promptChoice:
{{- $machineType := promptChoiceOnce "machine_type" "Machine type" (list "home" "client" "work") -}}
[data]
machine_type = {{ $machineType | quote }}
On first init, chezmoi will display:
Machine type? [home, client, work]:
The answer gets written into chezmoi.toml and becomes available as .chezmoi.config.data.machine_type in all your templates. Every conditional, every script, every external — they all key off this single prompt.
promptChoiceOnce only asks on first init. On subsequent runs of chezmoi init, it reuses the stored value from chezmoi.toml. Use plain promptChoice if you want to ask every time.
In your source state root, create the config template:
chezmoi cd
cat > .chezmoi.toml.tmpl << 'EOF'
{{- $machineType := promptChoiceOnce "machine_type" "Machine type" (list "home" "client" "work") -}}
{{- $email := promptStringOnce "email" "Git email address" -}}
[data]
machine_type = {{ $machineType | quote }}
email = {{ $email | quote }}
EOF
exit
Re-initialize to trigger the prompts:
chezmoi init
You'll see the prompts appear. Answer them, then verify the generated config:
cat ~/.config/chezmoi/chezmoi.toml
You should see your answers stored as TOML data. Every template in your repo now has access to these values.
chezmoi cd
git add .chezmoi.toml.tmpl
git commit -m "feat: add init config template with machine type prompt"
git push
exit
The generated chezmoi.toml is machine-specific output — it contains this machine's answers. Only commit the .chezmoi.toml.tmpl template. The actual config is generated fresh on each machine during init.
When that one-liner runs on a new machine, here's what each lesson contributes:
| Lesson | What it provides at bootstrap |
|---|---|
| 1. Init & Add | The source state structure and managed files |
| 2. Templates | Machine-specific rendering via .chezmoi.os and config data |
| 3. Scripts | run_once_ scripts install packages and configure the system |
| 4. Secrets | Secret manager integration pulls credentials securely |
| 5. Ignore & Remove | Platform-specific files get skipped or cleaned |
| 6. Multiple Machines | Conditionals produce the right config per machine type |
| 7. Git Workflow | The repo that init clones and keeps in sync |
| 8. Encryption | Encrypted files decrypt automatically during apply |
| 9. External Files | Plugins, themes, and tools download from their sources |
One command. Fully configured machine. Every tool installed, every secret in place, every config tuned for this specific machine type.
Name your install scripts so they run in the right order. run_once_01-install-packages.sh runs before run_once_02-configure-shell.sh. The lexicographic ordering of filenames determines execution sequence.
Use a VM or container to test the full flow before you need it for real. Run the one-liner in a clean environment and verify everything lands correctly. The worst time to discover a broken template is when you're setting up a new machine under pressure.
--apply flag do in the bootstrap one-liner?--apply, init would only clone the repo and generate config. The --apply flag tells chezmoi to also deploy all managed files to the target state in one shot.--apply flag combines init and apply into one step. After cloning the repo and generating config, it immediately renders all templates and deploys files to your home directory..chezmoi.toml.tmpl be placed in your source state?.chezmoi.toml.tmpl file lives at the root of your source state (~/.local/share/chezmoi/.chezmoi.toml.tmpl). Chezmoi looks for it there during init..chezmoi.toml.tmpl at the top level during init to generate the machine's config file.promptChoiceOnce and promptChoice?promptChoiceOnce checks if the value already exists in chezmoi.toml and skips the prompt if so. This means re-running chezmoi init won't re-ask questions you've already answered.promptChoiceOnce reads from the existing config if the key is already set, avoiding repeated prompts. promptChoice always asks, which is useful if you want to allow changing the value on re-init.You now have a complete multi-machine dotfiles system. One repo, one command, every machine configured exactly how you want it. Templates handle the differences, scripts handle the setup, and chezmoi handles the orchestration. Your future self on a fresh machine will thank you.
You've covered the full chezmoi workflow. From here, it's about refining your setup over time:
chezmoi: Set Up a New Machine With a Single Command — The official docs for the bootstrap flow, including variations for private repos and non-GitHub hosts.