Skip to main content
chezmoi uses a declarative approach to manage your dotfiles. It computes the target state for the current machine and then updates the destination directory to match.

Core concepts

The directory that chezmoi manages, usually your home directory (~).
A file, directory, or symlink in the destination directory that chezmoi manages.
The current state of all the targets in the destination directory.
Declares the desired state of your home directory, including templates that use machine-specific data. It contains only regular files and directories.
Where chezmoi stores the source state. By default it is ~/.local/share/chezmoi.
Contains machine-specific data. By default it is ~/.config/chezmoi/chezmoi.toml.
The desired state of the destination directory. It is computed from the source state, the config file, and the destination state. The target state includes regular files and directories, and may also include symbolic links, scripts to be run, and targets to be removed.
The git working tree. Normally it is the same as the source directory, but can be a parent of the source directory.

State flow diagram

Here’s how chezmoi transforms your source state into your destination state:

Understanding entries

chezmoi uses the generic term entry to describe something that it manages. Entries can be:
  • Files
  • Directories
  • Symlinks
  • Scripts
  • Remove operations

Entry types in source state

SourceStateFile

Regular files in the source directory. Include attributes parsed from the file name (like dot_, executable_, encrypted_).

SourceStateDir

Directories in the source directory. Include attributes parsed from the directory name (like exact_, private_).

Entry types in target state

TargetStateFile

A file with contents and permissions in the destination.

TargetStateDir

A directory in the destination.

TargetStateSymlink

A symbolic link in the destination.

TargetStateScript

A script that should be executed.

TargetStateRemove

An entry that should be removed from the destination.

Actual state entries

The actual state represents what currently exists in your destination directory.
  • ActualStateAbsent: Entry does not exist
  • ActualStateDir: Entry is a directory
  • ActualStateFile: Entry is a file
  • ActualStateSymlink: Entry is a symlink

Source state attributes

chezmoi encodes metadata about files using special prefixes and suffixes in filenames. This is how a regular file becomes a dotfile, executable, or template.

Common prefixes

PrefixEffectExample
dot_Rename to use a leading dotdot_bashrc.bashrc
executable_Add executable permissionsexecutable_script.sh → executable file
private_Remove all group and world permissionsprivate_key → mode 600
readonly_Remove all write permissionsreadonly_config → read-only file
encrypted_File is encrypted in source stateencrypted_secrets.txt.age
empty_Ensure file exists even if emptyempty_file
create_Create if doesn’t exist, don’t modify if it doescreate_config.json
modify_Run as script to modify existing filemodify_settings.sh
remove_Remove the entry if it existsremove_oldfile
run_Execute as a scriptrun_setup.sh
symlink_Create a symbolic linksymlink_config
exact_Remove anything not managed by chezmoiexact_directory/

Script-specific prefixes

run_once_

Only run if contents haven’t been run successfully before.

run_onchange_

Run whenever the script contents change.

before_

Execute before updating files.

after_

Execute after all files are updated.

Common suffixes

SuffixEffect
.tmplTreat file contents as a Go template
.literalStop parsing suffix attributes

Prefix order matters

Prefixes must be in the correct order. chezmoi will not recognize incorrectly ordered attributes.
encrypted_private_readonly_empty_executable_dot_filename.tmpl
Order: encrypted_private_readonly_empty_executable_dot_

The apply workflow

When you run chezmoi apply, here’s what happens:
1

Read source state

chezmoi reads all files and directories from the source directory (~/.local/share/chezmoi).
2

Parse attributes

For each entry, chezmoi parses the filename to extract attributes (prefixes and suffixes).
3

Execute templates

Files with .tmpl suffix are executed as Go templates using data from the config file and built-in variables.
4

Compute target state

Each source state entry computes its corresponding target state entry (what it should be in your home directory).
5

Read actual state

chezmoi reads the current state of each target in the destination directory.
6

Compare states

chezmoi compares the target state with the actual state to determine what changes are needed.
7

Apply changes

chezmoi applies the minimal set of changes to make the actual state match the target state:
  • Create missing files/directories
  • Update modified files
  • Remove extra files (in exact_ directories)
  • Execute scripts
  • Update permissions
8

Update persistent state

chezmoi stores the SHA256 hash of each entry it writes in persistent state for future change detection.
All file operations are atomic. You will never be left with incomplete files, even if the process is interrupted.

System abstraction

chezmoi abstracts all operating system interactions through a System interface. This enables powerful features:

System implementations

The actual underlying system that performs real file operations.
Wraps RealSystem for --dry-run mode. Allows reads to pass through but silently discards all writes.
Wraps RealSystem for --debug mode. Logs all calls to the underlying system.
This layered architecture is what makes features like --dry-run and --debug possible without modifying the core logic.

Persistent state

chezmoi maintains a persistent state to track what it has managed. This is stored as a two-level key-value store:
map[Bucket]map[Key]Value

Why persistent state?

Change detection

Detect if a third party has modified a file since chezmoi last wrote it.

Script execution

Track which run_once_ and run_onchange_ scripts have been executed.

EntryState storage

chezmoi stores the SHA256 hash of each entry’s contents in persistent state, rather than the full contents, to avoid storing secrets.

Path handling

chezmoi uses type-safe path handling to prevent errors:
  • AbsPath: Absolute paths
  • RelPath: Relative paths
  • SourceRelPath: Relative path within the source directory with attribute handling
  • ExtPath: External paths from user input (may include ~ for home directory)
Internally, chezmoi normalizes all paths to use forward slashes and is case-sensitive. It makes no attempt to handle case-insensitive or case-preserving file systems.

Execution order

chezmoi performs actions in a deterministic order:
1

Before scripts

Execute all run_before_ scripts in ASCII order of their target names.
2

Files, directories, symlinks

Process in ASCII order of target names.
3

Regular scripts

Execute run_ scripts (without before_ or after_) in ASCII order alongside files.
4

After scripts

Execute all run_after_ scripts in ASCII order of their target names.
Example: Given dot_a (file), run_z.sh (script), and exact_dot_c/ (directory), chezmoi will:
  1. Create .a
  2. Create .c/ directory
  3. Execute run_z.sh

Encryption support

Encryption tools are abstracted through an Encryption interface:

AGE encryption

Modern, simple file encryption with age.

GPG encryption

Traditional encryption with GnuPG.
Encrypted files in the source state have their encryption suffix (.age or .asc) automatically stripped when determining the target name.

Interactive workflow diagram

Key takeaways

Declarative: You specify what you want, chezmoi figures out how to get there.
Idempotent: Running chezmoi apply multiple times produces the same result.
Safe: Dry-run mode, atomic operations, and persistent state tracking prevent accidents.
Flexible: Templates, attributes, and scripts provide powerful customization.
Ready to dive deeper? Explore the configuration reference for complete details on all attributes, template functions, and configuration options.

Build docs developers (and LLMs) love