A sudo wrapper for chezmoi to manage root-owned files across your entire
filesystem.
chezroot is a companion wrapper that extends chezmoi's power to your system-level
files, allowing you to manage files in /etc, /Library/LaunchDaemons, or anywhere
else on your root filesystem with the same chezmoi workflow.
Since chezroot is a wrapper and not a reimplementation, it passes most commands,
flags, and configuration options directly to the underlying chezmoi binary.
This documentation focuses on features, limitations, and design choices that are
specific to chezroot (like the profile system, sudo handling, and unsupported
commands). For all standard chezmoi functionality such as template functions,
editor selection logic, or git integration, refer to the official chezmoi
documentation.
- Features
- Platform Support
- Installation
- Usage
- Configuration
- Profiles
- Passwordless sudo
- Design
- Acknowledgements
- Manage the Entire Filesystem: Manages files anywhere, from files in
/etcto/Library/LaunchDaemons. - Isolated Profiles: Manage multiple, independent configurations (e.g., from different repositories) using the --profile option. Each profile's source, config, state, and cache are stored in separate, dedicated directories, ensuring they never conflict.
- Safe
sudoExecution: Intelligently wrapschezmoicommands insudoonly when needed. - User-Level Actions: Automatically drops privileges for commands like
editandcd, so you always edit configuration files as your normal user, not as root. - Safe by Default:
chezroot initautomatically creates a.chezmoiignorefile that ignores everything. You must explicitly un-ignore the files you wish to manage. - No External Library Dependencies:
chezrootis a single POSIX-compliant shell script with no dependencies other thanchezmoi,sudo, and common system utilities (bash,sed)." - Cross-Platform: Works seamlessly on both Linux and macOS.
chezroot is designed exclusively for POSIX-like systems (Linux and macOS) that use
sudo for privilege escalation. It relies on bash, sudo, and standard Unix
utilities to manage file ownership and drop privileges.
Windows is not a supported platform. The Windows administrative model (UAC, "Run
as Administrator") is fundamentally different from sudo, and chezroot's bash
scripts are not compatible with PowerShell or the Windows filesystem.
While chezmoi itself works perfectly on Windows, chezroot cannot be used to wrap
it there.
Installation is handled by a package manager appropriate for your platform.
You can install chezroot from the official Homebrew tap:
brew tap jcouball/tap
brew install chezrootPackages for popular Linux distributions are in progress.
# Package coming soon
yay -S chezroot# PPA coming soon
sudo add-apt-repository ppa:jcouball/ppa
sudo apt update
sudo apt install chezroot# COPR repository coming soon
sudo dnf copr enable jcouball/chezroot
sudo dnf install chezrootThe workflow is nearly identical to chezmoi, with one extra safety step. By
default, you will be operating on the default profile, which is stored in
~/.local/share/chezroot/default.
This creates your source directory at ~/.local/share/chezroot/default.
$ chezroot init
info: Running post-init setup...
info: Creating safe-by-default .chezmoiignore...
info: Creating .chezmoiversion...chezroot is safe by default. It will ignore all files until you explicitly
allow them.
Edit the newly created ignore file at
~/.local/share/chezroot/default/.chezmoiignore.
The file will contain a single * (ignore all). To manage /etc/hosts and all files
under /etc/nginx, you would change it to:
# .chezmoiignore
# 1. Ignore everything by default
*
# 2. Un-ignore the specific files and directories
# you explicitly want to manage.
!/etc/hosts
!/etc/nginxNow you can use the standard chezmoi workflow.
# Add a file from the root filesystem to your source repo
$ chezroot add /etc/hosts
# Edit the file (this runs $EDITOR as your user, not root)
$ chezroot edit /etc/hosts
# See what changes will be applied
$ chezroot diff
# Apply the changes to your system (this runs with sudo)
$ chezroot -v applychezroot is configured primarily via command-line options and environment variables.
-
--profile <name>Specifies the profile to use. This overrides the
$CHEZROOT_PROFILEenvironment variable if it is also set.See the "Profiles" section for details.
-
--configand--config-formatare not supportedchezmoi's--configand--config-formatoptions are not supported.chezroot's core "Profile" feature works by strictly managing all paths for you. Each profile's configuration is always loaded from its dedicated directory (e.g.,~/.config/chezroot/$PROFILE/) to ensure complete isolation. Allowing an external config file would break this model and create a conflict with the--profileoption.
-
$CHEZROOT_PROFILEDefault: (unset)
Specifies the profile to use if the
--profileoption is not provided. This is a convenient way to work with a specific profile for an entire terminal session.If neither the
--profileoption nor this variable are set,chezrootwill use the profile namedefault.
By default, chezroot manages a single profile (a self-contained set of
configurations). The default profile is named default and is stored in
~/.local/share/chezroot/default.
The current profile in use is controlled using the --profile option or, as a
convenient default, the $CHEZROOT_PROFILE environment variable. If neither of these
is specified the default profile is used.
This is useful if you want to manage different sets of root file configurations from
different git repositories. For example, you could have one repository for your
system-wide shell settings and a completely different repository for managing nginx
configurations.
Let's set up a new profile called nginx to manage web server files.
-
Initialize the new profile:
This command will use the profile name
nginxto create a new source directory at~/.local/share/chezroot/nginxand link it to your new repository.chezroot --profile nginx init https://github.com/user/nginx-configs.git
-
Add files to the new profile:
All subsequent commands must use the
--profile nginxoption to operate on this new instance.chezroot --profile nginx add /etc/nginx/nginx.conf
-
Use the default profile:
To go back to managing your default files, simply run
chezrootwithout the option.chezroot apply
This works because each profile maintains its own separate configuration, source directory, and state file, so they do not interfere with each other.
-
Using the Environment Variable for Convenience
If you are going to work on the nginx profile for a while, you can set the environment variable for your session instead of typing the option repeatedly. The
--profileoption will always override this variable if you need to switch temporarily.export CHEZROOT_PROFILE=nginx chezroot add /etc/nginx/sites-available/default # Uses 'nginx' profile chezroot apply # Also uses 'nginx' profile
chezroot provides a dedicated profile command to list, inspect, and delete your
profiles.
A profile consists of four distinct directory paths:
- Source: $HOME/.local/share/chezroot/$PROFILE/
- Config: $HOME/.config/chezroot/$PROFILE/
- State: $HOME/.local/state/chezroot/$PROFILE/
- Cache: $HOME/.cache/chezroot/cache/$PROFILE/
Lists the names of all discovered profiles. This command scans the chezroot config
and data directories and prints a list of all profile names it finds.
Example:
$ chezroot profile list
default
nginx
webserverDisplays the four filesystem paths associated with one or more profiles. This is a read-only command used to inspect the exact directories that make up a profile.
Example:
$ chezroot profile info default nginx
Profile: default
Config: /Users/james/.config/chezroot/default
Source: /Users/james/.local/share/chezroot/default
State: /Users/james/.local/state/chezroot/default
Cache: /Users/james/.cache/chezroot/cache/default
Profile: nginx
Config: /Users/james/.config/chezroot/nginx
Source: /Users/james/.local/share/chezroot/nginx
State: /Users/james/.local/state/chezroot/nginx
Cache: /Users/james/.cache/chezroot/cache/nginxPermanently delete all data associated with one or more profiles.
This is a destructive operation. It will permanently delete the profile's Source, Config, State, and Cache directories and all their contents from your local disk. It will prompt for confirmation before deleting anything.
Options:
| Flag | Shorthand | Description |
|---|---|---|
| --force | -f | Skips the confirmation prompt. |
| --dry-run | -n | Prints the paths that would be deleted without actually deleting them. |
Example:
$ chezroot profile purge nginx
The following 4 directories and all their contents will be PERMANENTLY DELETED:
- /Users/james/.config/chezroot/nginx
- /Users/james/.local/share/chezroot/nginx
- /Users/james/.local/state/chezroot/nginx
- /Users/james/.cache/chezroot/cache/nginx
Are you sure? [y/N] y
info: Purged profile 'nginx'.To avoid typing your password for every apply or diff, it is highly recommended
to add a sudoers rule.
Create a new file at /etc/sudoers.d/chezroot (as root) with the following content.
Be sure to replace YOUR_USER and use the correct absolute path to the
chezroot script (which you can find with which chezroot).
# /etc/sudoers.d/chezroot
YOUR_USER ALL=(root) NOPASSWD: /usr/local/bin/chezrootThis rule allows your user to run only the chezroot script as root without a
password.
The chezroot command line tool is a wrapper around chezmoi. It aims to be fully
compatible with chezmoi with a few exceptions.
chezroot implements profiles to allow multiple chezmoi-type sources which can be
shared and can configure specific things within the host operating system. For
example, one source might configure a webserver, another might configure system-wide
shell settings, etc.
The current profile is set at the command line with the --profile option or
via the environment variable $CHEZROOT_PROFILE. If a profile is not specified, The
profile default is used.
Profiles are used to create isolation from each other. Each profile has its own source directory, config file, persistent state file, and cache as follows:
- Source Directory:
$HOME/.local/share/chezroot/$PROFILE/ - Config File:
$HOME/.config/chezroot/$PROFILE/config.* - Persistent State File:
$HOME/.local/state/chezroot/$PROFILE/state.boltdb - Cache Directory:
$HOME/.cache/chezroot/cache/$PROFILE/
The path to the configuration file passed to chezmoi is determined by the profile
and loaded from $HOME/.config/chezroot/$PROFILE/.
Because chezroot must pass an explicit file path to chezmoi (using the --config
flag internally), it automatically discovers the correct configuration file by
searching that directory for config.toml, config.json, config.yaml, or
config.jsonc.
For this reason, the user cannot specify their own path. Use of the chezmoi command
line options -c, --config, and --config-format are not allowed.
To prevent ambiguity, it is an error to have more than one configuration file in a
single profile directory. If chezroot finds both config.toml and config.yaml,
for example, it will abort and report an error.
The path to the source directory passed to chezmoi is also determined by the profile.
The source directory is $HOME/.local/share/chezroot/PROFILE/.
The user cannot change the path to the source directory. Use of the chezmoi command
line options -S and --source-directory are not allowed. If sourceDir is given
in the configuration file, it is ignored.
The source directory and its contents are assumed to be owned by the user
invoking the chezroot command.
The destination directory path is set to "/".
The user cannot change the destination directory path. Use of the chezmoi command line options -D and --destination-directory are not allowed. If destDir is given in the configuration file, it is ignored.
The destination files are assumed to be owned by root.
chezroot is designed to be invoked as a non-root user. Internally, chezroot will
run chezmoi and other commands with sudo as needed.
chezroot explicitly unsets any CHEZMOI_* environment variables in the execution
environment before invoking the underlying chezmoi command. This prevents environment
variables from the user's chezmoi setup from interfering with chezroot.
chezroot then reconstructs the CHEZMOI_* variables for the chezmoi subprocess
by mapping CHEZROOT_* variables in two stages:
-
Global variables:
- Variables prefixed with
CHEZROOT_are mapped - For each
CHEZROOT_VAR=value, the correspondingCHEZMOI_VAR=valueis set. (Example:CHEZROOT_VERBOSE=truesetsCHEZMOI_VERBOSE=true).
- Variables prefixed with
-
Profile-Specific variables:
- Variables prefixed with
CHEZROOT_PROFILE_${PROFILE}_(where${PROFILE}is the name of the currently active profile) are mapped. - For each
CHEZROOT_PROFILE_${PROFILE}_VAR=value, the correspondingCHEZMOI_VAR=valueis set, overwriting any value set by a global variable. (Example: If the active profile isweb, thenCHEZROOT_PROFILE_WEB_VERBOSE=falsesetsCHEZMOI_VERBOSE=false, taking precedence over anyCHEZROOT_VERBOSEsetting).
- Variables prefixed with
Profile-specific variables always take presedence over global variables.
chezroot does not manipulate the PATH variable when running sudo chezmoi.
Most sudo configurations reset the PATH to a predefined, secure set of directories
(often defined by secure_path in /etc/sudoers). This is a security measure to
prevent users from executing arbitrary commands as root just because those commands
happen to be in their personal PATH.
Users should configure absolute paths for any external commands (diff tools, merge tools, git, password managers, encryption tools, etc.) within their chezroot profile's configuration file if those commands are needed during operations that require sudo.
chezroot invokes the underlying chezmoi command without explicitly changing the
current working directory (CWD) from where chezroot itself was run.
- Command line arguments, including any relative paths (e.g.,
chezroot add ./file), are passed as-is bychezrootto the underlyingchezmoicommand. chezmoiwill interpret these relative paths based on its own CWD at the time of execution.- Important: When
chezrootusessudoto runchezmoi,sudomay change the CWD beforechezmoiexecutes (often to/root). This means relative paths provided on the command line might not resolve as expected in commands requiringsudo(likeadd,apply). - Therefore, it is strongly recommended to use absolute paths when referring to
files on the command line with
chezroot, especially for commands that might run withsudo. - Similarly, users should be cautious when using relative paths inside
run_scripts, as the CWD during their execution (especially undersudo) might not be the directory wherechezrootwas invoked. Using absolute paths or determining paths dynamically within scripts is generally safer.
chezroot manages target files that are owned by root. This means that any chezmoi
command that may read or update target files in the destination directory must be
executed with sudo. This includes commands the evaluate templates which may use
template functions that read the target state (i.e, the stat function).
Commands executed WITH sudo are:
- add, apply, cat, destroy, diff, dump, execute-template, init (only when
--applyor--one-shotis used), merge, merge-all, purge, re-add, status, unmanaged, update, verify
Commands executed WITHOUT sudo are:
- cat-config, cd, chattr, completion, data, decrypt, dump-config, edit, edit-config, edit-config-template, encrypt, forget, generate, git, help, ignored, import, license, list, managed, secret, source-path, state, target-path, unmanage
Unsupported commands:
- docker, ssh, upgrade
The following are all expected to be owned by the user who invokes chezroot:
- the source directory and its contents recursively
- the cache directory and its contents recursively
- the persistent state file
Many chezmoi commands must be run with sudo which can leave modified files with
root ownership. To prevent this, chezroot will automatically "fix ownership" of
these files. This means that it will chown all affected files and directories back
to the invoking user after any sudo command completes.
chezroot will automatically fix ownership after running the following commands:
- add, apply, destroy, merge, merge-all, purge, re-add, update
Some commands may need additional special handling. The rest of this section gives those details.
The edit options --apply and --watch are not currently supported. Instead run
chezroot edit ... and then chezroot apply as two separate commands.
These options are unsupported because they create a permission conflict. The edit
action must run as your normal user to access your editor and source files, but the
apply action must run with sudo to write to the root filesystem.
chezroot will pass the option --guess-repo-url=false to chezmoi init. This option
is passed as a safety measure to disable chezmoi's default behavior of guessing a
repository URL (like github.com/user/dotfiles). This ensures you explicitly provide
the full URL for your system-level configuration, rather than accidentally using your
personal dotfiles repository.
chezmoi init will be run with sudo only if the --apply or --one-shot option is
passed. In this case, chezroot will fix ownership after sudo chezmoi init
completes.
If $HOME/.local/share/chezroot/$PROFILE/.chezmoiignore does not exist, chezroot
will create an ignore file that ignores everything. You must explicitly un-ignore the
files you wish to manage. It also ignores the config.
If $HOME/.local/share/chezroot/$PROFILE/.chezmoiversion does not exist, chezroot
will create the file containing the minimal version required for chezroot. If the
file exists, chezroot will ensure the version is compatible with the minimal
version required.
- This tool is a simple wrapper and would not be possible without the excellent chezmoi by Tom Payne.
- This project borrows from the excellent chezetc by Shengyu Zhang