Bootstrap a New Machine

Lesson 10 · chezmoi · ~12 minutes

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.

The One-Liner

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:

  1. Downloads and installs the chezmoi binary
  2. Clones your dotfiles repo from github.com/YOUR_GITHUB_USERNAME/dotfiles
  3. Prompts you for any config values (like machine type)
  4. Runs your run_once_ and run_onchange_ scripts
  5. Applies all templates with the new machine's config

If 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 Full Bootstrap Flow

What happens in that one command
Install
Download chezmoi binary
Clone
Pull repo as source state
Prompt
Ask config questions
Scripts
Run install scripts
Apply
Render & deploy all files

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.

The Init Config Template

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 vs promptChoice

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.

Exercise: Create your init config template

Step 1: Create .chezmoi.toml.tmpl

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

Step 2: Test it locally

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.

Step 3: Commit the template

chezmoi cd
git add .chezmoi.toml.tmpl
git commit -m "feat: add init config template with machine type prompt"
git push
exit
Don't add chezmoi.toml itself

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.

How Everything Connects

When that one-liner runs on a new machine, here's what each lesson contributes:

LessonWhat it provides at bootstrap
1. Init & AddThe source state structure and managed files
2. TemplatesMachine-specific rendering via .chezmoi.os and config data
3. Scriptsrun_once_ scripts install packages and configure the system
4. SecretsSecret manager integration pulls credentials securely
5. Ignore & RemovePlatform-specific files get skipped or cleaned
6. Multiple MachinesConditionals produce the right config per machine type
7. Git WorkflowThe repo that init clones and keeps in sync
8. EncryptionEncrypted files decrypt automatically during apply
9. External FilesPlugins, 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.

Tips for a Smooth Bootstrap

Order matters for scripts

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.

Test your bootstrap

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.

Knowledge check

What does the --apply flag do in the bootstrap one-liner?
Correct. Without --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.
The --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.
Where should .chezmoi.toml.tmpl be placed in your source state?
Right. The .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.
It belongs at the root of your source state directory. Chezmoi specifically looks for .chezmoi.toml.tmpl at the top level during init to generate the machine's config file.
What is the difference between promptChoiceOnce and promptChoice?
Exactly. 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.

🎉 Course Complete

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.

Where to go from here

You've covered the full chezmoi workflow. From here, it's about refining your setup over time:

Recommended Reading

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.

Questions? Ask me anything about your bootstrap setup. I can help troubleshoot init templates, debug script ordering, or review your full chezmoi configuration.
← Prev