Skip to content

Chezmoi Script Lifecycle

Execution Phases

During chezmoi apply, scripts run in three phases:

  1. before scripts execute
  2. Managed files are applied
  3. after scripts execute

Scripts without an explicit before_ or after_ in the name run interleaved with managed files in alphabetical order by target name. Always use explicit before_ or after_ prefixes to avoid ambiguity.

Chezmoi naming

A source file is what lives in this repo. Chezmoi derives a target name by stripping special prefixes and suffixes from the source filename. For managed files, the target is the real destination in $HOME (e.g., dot_zshrc.tmpl~/.zshrc). For scripts, the target name is only used for sort ordering — no file is created (e.g., run_once_before_00-install-brew.sh.tmpl00-install-brew.sh).

Source Attributes

Source attribute Effect
dot_ Replaced with .
run_ Marks file as a script
once_, onchange_ Script execution condition
before_, after_ Script execution phase
executable_ Sets execute permission
private_ Removes group/world permissions
exact_ Removes unmanaged files in target directory
create_ Only writes if target doesn't exist
modify_ Runs as a modify template (receives existing content via stdin)
.tmpl suffix Processes file as a template

See chezmoi source state attributes for the full reference.

Execution Conditions

Prefix Behaviour
run_ Runs on every chezmoi apply
run_once_ Runs once (tracked by content hash)
run_onchange_ Runs when the rendered script content changes

Combined Prefixes

Timing and condition combine as:

Prefix When How often
run_once_before_ Before files Once
run_once_after_ After files Once
run_onchange_before_ Before files On content change
run_onchange_after_ After files On content change
run_before_ Before files Every apply
run_after_ After files Every apply

Ordering

Within each phase, scripts run in alphabetical order by filename. Use numeric prefixes to control this:

run_once_before_00-install-brew.sh      ← runs first (before phase)
run_once_before_01-setup-zshrc.sh       ← runs second (before phase)
  ← managed files applied →
run_once_after_10-brew-bundle.sh        ← runs first (after phase)
run_after_20-setup-dotfiles-repo.sh     ← runs second (after phase)
run_onchange_after_30-update-fonts.sh   ← runs third (after phase)
run_once_after_99-reminders.sh          ← runs last (after phase)

The once/onchange/always distinction does not affect sort order — all scripts within a phase are sorted together.

Conventions

See Code Style for scripting rules (idempotency, error handling, platform checks). Use custom template delimiters per the guidelines.