Managing our dotfiles across multiple machines and operating systems can be complex. A tool designed for this purpose, chezmoi, can streamline this process and store the dotfiles in a GitHub repository, manage the differences between systems (using a template), and secure some of those files using encryption.
Managing our dotfiles across multiple machines and operating systems can be complex. A tool designed for this purpose, chezmoi, can streamline this process and store the dotfiles in a GitHub repository, manage the differences between systems (using a template), and secure some of those files using encryption.
Revision: 20241108-0 (init: 20241101)
ChezMoi is a versatile tool designed to manage our personal configuration files, or dotfiles, across multiple machines and operating systems. It enables us to maintain a consistent environment by seamlessly synchronizing configurations like .zshrc or .gitconfig. Its key features include templating to handle machine-specific differences, integration with various password managers for secure secret management, and support for full file encryption using tools like GPG or age.
chezmoi supports age for encryption. Age is a modern, user-friendly encryption tool and Go library designed for secure file encryption, and it emphasizes simplicity and security by utilizing small, explicit keys and eliminating complex configuration options.
This command creates a new Git repository in ~/.local/share/chezmoi, where chezmoi stores its source state.
💡
We note that in order to push/pull from GitHub you will have to have an authorized local ssh key.
For persistence, we will add it to a new dotfiles GitHub repository. This repository can be public or private. Other Git-compatible sites such as GitLab, Gitea or BitBucket will work as well. After creating our new dotfiles repository for our example user, we must configure chezmoi to use this remote repository:
# cd into chezmoi's directory (at ~/.local/share/chezmoi)
chezmoi cd
# adapt EXAMPLE to match your repository
git remote add origin [email protected]:EXAMPLE/dotfiles.git
git branch -M main
# add a single file (that will exist as is), for example ~/.gitconfig
chezmoi add ~/.gitconfig
# Add it to git
git add dot_gitconfig
git commit -m "Adding .gitconfig"
# Push it to the remote repo
git push -u origin main
We see that the file named .gitconfig was renamed by ChezMoi as dot_gitconfig. Chezmoi uses special prefixes and suffixes in filenames to encode metadata and attributes about how files should be managed, such as permissions, encryption, and templating.
chezmoi’s file naming
File naming conventions in the chezmoi source directory are crucial for defining how files are managed and applied to the destination directory. These conventions use specific prefixes and suffixes to indicate the desired behavior for each file or directory. Among the common prefixes are:
dot_ is replaced with a . in the destination directory. For example, a source file named dot_bashrc becomes .bashrc in the destination.
private_: Sets the file or directory's permissions to be private (i.e., readable and writable only by the owner); i.e. private_dot_ssh ensures the .ssh directory has restricted permissions.
executable_ makes the file executable, such that executable_dot_script.sh results in a .script.sh file with execute permissions.
symlink_ to create a symbolic link at the destination. The file's contents specify the link's target. For example, a symlink_dot_config whose file content is /etc/config would create a symlink for a .config pointing to /etc/config.
run_designates the file as a script to be executed. For example as run_install.sh would be executed during the apply process.
.tmplindicates that the given file is a template, allowing dynamic content generation based on variables. A dot_gitconfig.tmpl would be processed as a template to produce .gitconfig (see more details on file templates for ChezMoi at https://www.chezmoi.io/user-guide/templating/ and in the later chezmoi add section of this document).
chezmoi cd
# Note: the following files are in chezmoi's local git repository to be uploaded to GitHub
# after git add, git commit and git push are performed
# Generate an age private key encrypted with a passphrase in the file key.txt.age
age-keygen | age --armor --passphrase > key.txt.age
# make a note of the public key and passphrase as those will be needed later
# Add key.txt.age to .chezmoiignore to avoid it being copied out of the chezmoi location
echo key.txt.age >> .chezmoiignore
# create a template script to decrypt the passphrase-encrypted private key if needed:
cat > run_once_before_decrypt-private-key.sh.tmpl <<EOF
#!/bin/sh
if [ ! -f "${HOME}/.config/chezmoi/key.txt" ]; then
mkdir -p "${HOME}/.config/chezmoi"
chezmoi age decrypt --output "${HOME}/.config/chezmoi/key.txt" --passphrase "{{ .chezmoi.sourceDir }}/key.txt.age"
chmod 600 "${HOME}/.config/chezmoi/key.txt"
fi
EOF
Create a ~/.config/chezmoi/chezmoi.toml file (making sure encryption is at the top of the file, before any other section), adapting the AGE_PUBLIC_KEY with the value obtained earlier:
Use chezmoi to generate its configuration file and decrypt the private key; we will be prompted for the secret passphrase (make sure to store it is a safe location such as a password manager, as it will be needed for any new setup).
chezmoi init --apply
This will create a ${HOME}/.config/chezmoi/key.txt file containing the decrypted private key on that system.
The final step of the encryption setup is to add the four files present in the chezmoi cd location to GitHub.
chezmoi cd
# Copy the newly created chezmoi configuration file to the chezmoi source path
# per https://github.com/twpayne/chezmoi/issues/3638#issuecomment-1986937646
cp ~/.config/chezmoi/chezmoi.toml "$(chezmoi source-path)/.chezmoi.toml.tmpl"
git status
# should report: .chezmoi.toml.tmpl .chezmoiignore key.txt.age
# and: run_once_before_decrypt-private-key.sh.tmpl
# "git" add, commit, and push the files to GitHub
git add .
git commit -m "age set additions"
git push
Adding new files: chezmoi add
The chezmoi add command is used for incorporating existing files and directories into ChezMoi. The command offers various options to handle different scenarios:
to add regular files, usechezmoi add ~/.bashrc which will copy the current state of ~/.bashrc into chezmoi's source directory
adding files as Templates: If a file contains machine-specific configurations, chezmoi add --template ~/.gitconfig will creates a template version of ~/.gitconfig, allowing for dynamic content based on variables, such as {{ .chezmoi.hostname }} or more complex if ... else selections such as:
{{ if eq .chezmoi.os "darwin" }}
# darwin
{{ else if eq .chezmoi.os "linux" }}
# linux
{{ else }}
# other operating system
{{ end }}
ChezMoi uses Go's text/template syntax extended with functions from the sprig library.
Files are interpreted as templates if they have a .tmpl suffix or are located in the .chezmoitemplates directory.
The .chezmoitemplates directory can be used to store reusable template snippets that can be included in other templates.
Template data can come from various sources, including ChezMoi's built-in variables, user-defined data in configuration files, and runtime data from password managers or environment variables.
Templates can be tested and debugged using the chezmoi execute-template command.
to automatically generate Templates (will be replacing strings that match variable values from the data section of the config file) use chezmoi add --autotemplate ~/.gitconfig which implies the --template option and attempts to create a template with variable substitutions.
Adding and encrypting sensitive files: chezmoi add --encrypt ~/.ssh/id_ed25519 encrypts the private SSH key using age, ensuring its security within the git repository and because of we used passphrase for age, the file is stored encrypted and protected by this passphrase (as always, protect your secrets).
Adding directories:
Recursively: chezmoi add --recursive ~/.vim includes all files and subdirectories within ~/.vim
prompting before adding each file: chezmoi add --prompt ~/.config interactively prompts before adding each file within ~/.config.
with exact matching: to ensure the destination directory exactly matches the source; this will remove any extra files not present in the source. chezmoi add --exact ~/.config
Adding symbolic links by following targets (if the target is a symlink and you want to add the actual file it points to): chezmoi add --follow ~/.config/symlinked_file adds the target of the symlink instead of the symlink itself.
After having added a few files and uploaded those on GitHub, we can use those dotfiles on other systems. Below is a list of some of the files on that GitHub repository, where we can see various dot_, .tmpl, .age, private_, encrypted_ and run_ ChezMoi files.
Although it is not needed right after the above git pull performed during the chezmoi init, for future uses, it is strongly recommended to perform a git pull before using other chezmoi commands to have any updates from the GitHun repository incorporated.
chezmoi cd
git pull
First, we need to extract the encrypted age key so we can check any changes before applying those changes. chezmoi apply would perform this as well but it would also force apply the changes. To be able to see what would be modified, we need to prepare chezmoi to recognize all our imported files.
chezmoi cd
chezmoi execute-template < ./run_once_before_decrypt-private-key.sh.tmpl > ~/chezmoi-decrypt.sh
chmod +x ~/chezmoi-decrypt.sh
~/chezmoi-decrypt.sh
# We will be prompted for the passphrase
# Check that the local decrypted key was generated
ls $HOME/.config/chezmoi/key.txt
# Erase the one-time use decryption script
rm ~/chezmoi-decrypt.sh
Use chezmoi status to obtain the list of files that would be modified by a chezmoi apply.
For each file, we can see if the file is to be Added, Modified or Removed. For those files that are modified, we can chezmoi diff to see the changes. For example if ~/.gitconfig appeared on the list, we can chezmoi diff ~/.gitconfig and see the changes. The + entries are changes from the chezmoi controlled files, while - are removals from the existing local files.
# Check the difference for modified files
chezmoi diff ~/.gitconfig
# + entries are additions in the chezmoi controlled files
# - are removals from the local file
# In some cases, before applying the chezmoi version of a file, we may want
# to incorporate content from the local files
# We must edit the chezmoi version of that file
EDITOR="nano" chezmoi edit ~/.gitconfig
# We can apply the modified (and maybe edit-ed) version
chezmoi apply ~/.gitconfig
# After this apply, when running git status
# the ~/.gitconfig file is now removed from that list
# We can then continue the diff/edit/apply steps
# until there are no more changes to review
# or we are comfortable with the leftover files to update
After performing the check for each file to be Modified, we can then apply any other changes:
chezmoi apply
Once we are done with changes (including altering templates as needed), we want to push those modifications back to GitHub
chezmoi cd
git status
# The following example only reflects a file modification to ~/.gitconfig
# For more, adapt the `git add` and `git commit` as needed
git add dot_gitconfig
git commit -m "Commit message"
# perform the push after the various add and commit are done
git push
# The expectation is that after the push, no files are left unaccounted for
git status
The following are best practices from using Unraid for close to three years. Although much more complex installation (with ZFS pools or complex alternate OSes VM setup) are possible, it is written to support people interested in learning about the tool. I wrote about Unraid in the past, such as “Things I wished I had READ before my first Unraid Install... and more”. This post is different as I have recently installed a new workstation; and used the experience to document various aspects such as installation, array management, share configurations, and community applications.
I hope it provides valuable insights to benefit those interested in exploring Unraid further, especially as the 7.0 version is coming soon. We note that we will use external links to Unraid’s documentation for additional content on discussed topics.
This post details using our VPS as a SOCKS5 Proxy for a single web browser and how to set up wg_easy to create a peer-to-peer network for accessing self-hosted services.
This is a more opinionated post than usual: I live on the command line on Linux.
As such, I perform a few extra steps after a fresh installation to get to a “usable” system.