TL;DR: After 12 years of manually creating symlinks to deploy my dotfiles on a variety of hosts, operating systems and distributions, I finally decided to switch to a dedicated tool to make my life easier. 🤖

1. Did you say “dotfiles”? Link to heading

On computer systems, configuration for command-line tools and applications is stored as files and directories, usually in the user’s home directory.

To avoid cluttering the display when navigating the filesystem, they are hidden by default; on UNIX-like systems, this is done by prefixing the name with a dot (.) —hence the name “dotfiles”.

You will find most of them:

  • in your home directory and the ~/.config/ directory on Linux distributions and macOS;
  • in the ~/Library/Application Support directory on macOS;
  • in the C:\Users\<username>\AppData\Roaming, C:\Users\<username>\AppData\Local and C:\Users\<username>\AppData\LocalLow directories on Windows.

To read more about dotfiles:

2. They come in all shapes and sizes Link to heading

Any given software may store its configuration as a combination of:

  • a single configuration file;
  • a configuration directory containing one or more files;
  • a mix of configuration files, local caches and databases.

So, we will need to handle:

  • deploying and updating our dotfiles;
  • excluding non-configuration files:
    • examples: caches, databases, sensitive information;
  • OS-specific locations:
    • example: depending on the OS, a given software may store its configuration in a different path;
  • distribution-specific locations and configuration
    • example: we get a different major version of a given software, and the configuration differs;
  • usage-specific configuration:
    • examples: work versus personal computer, server versus workstation versus laptop.

3. The Dark Ages™ Link to heading

When I started using Linux for software development circa 2009, I just had a couple hosts to take care of, and copy-pasting (or rsync-ing) was fine enough. Until it became a hot mess because some configuration file had been updated on both sides, and I ended up losing one of the versions… 😅

4. Keeping things under (version) control Link to heading

This is the first step towards sanity:

  1. create a Git repository, aptly named dotfiles;
  2. clone this repository under a well-known location, e.g. ~/.dotfiles or ~/.dots;
  3. add all dotfiles to the repository;
  4. wait, did we say “caches, databases, sensitive information”?
    1. add a top-level .gitignore file to ignore editor settings and swap files;
    2. add directory-specific .gitignore files to ignore undesired resources;
  5. create symbolic links (symlinks) for each and every configuration file and directory.

Are we done yet?

Not even close.

  • Windows requires privileged access to create symlinks;
  • macOS sometimes stores its configuration elsewhere;
  • different Linux distributions come with different major versions for software, that will require extra shenanigans such as maintaining different versions of the configuration, and choosing which one we will symlink to
  • what if we want to change our default $EDITOR from nano to neovim? or our $PAGER from more to bat?

For a while (2018-2025), I tried to circumvent these issues by using Ansible for local provisioning: virtualtam/anarres@8832620

Which helped with:

  • installing prerequisite software and tools on Linux hosts;
  • cloning and updating the dotfiles repository;
  • taking care of creating the (many) symlinks;
  • and, well, that’s it!

But had other limitations:

  • it did not take care of non-Linux hosts (macOS, Windows);
  • the dotfiles role needed to be updated each time new dotfiles were added or removed;
  • this was mostly useful for development workstations, where I had:
    • Ansible already installed;
    • SSH access to the dotfiles repository.

5. There’s no place like chez moi 🏡 Link to heading

5.1. The state of things Link to heading

At the time of writing, I am now managing configuration for a merry assortment of:

  • Arch Linux workstations: for software development and cross-compilation;
  • Debian servers: for hosting services;
  • Ubuntu LTS servers: for hosting specific services;
  • Ubuntu LTS workstations: for work-related usage;
  • macOS laptops: for work-related and so-called “digital nomad” usage;
  • Windows workstations: for light development work.

To say I was ripe for a change would be an understatement! 😂

5.2. Choosing a dotfiles manager Link to heading

There are multiple options available for managing dotfiles, so let’s start with our requirements; the ideal solution would:

  • be cross-platform;
  • be packaged for:
    • all major Linux distributions;
    • macOS (via Homebrew);
    • Windows (via Winget);
  • support OS-, distribution- and usage-specific configuration;
  • support templating, conditionals, loops and functions;
  • provide a user-friendly command-line interface (CLI) to:
    • preview and check changes;
    • help during migration;
    • help tackling specific situations.

After looking on the Internet, the most recommended solutions were:

  • chezmoi - Manage your dotfiles across multiple diverse machines, securely
  • Dotbot - A tool that bootstraps your dotfiles
  • GNU Stow - A symlink farm manager
  • rcm - rc file (dotfile) management
  • yadm - Yet Another Dotfiles Manager

I chose to go with chezmoi as it ticked the most boxes:

  • available for Linux, macOS and Windows;
  • packaged as a single binary, so easily deployable and updatable on older systems;
  • written in Go, and uses the Go templating syntax, which I am familiar with;
  • has an active community, great documentation and plenty of examples available.

5.3. Migrating my dotfiles Link to heading

As my dotfiles were already tracked in a Git repository, I followed these steps to make the migration as smooth as possible:

  1. Clone my dotfiles repository to ~/.local/share/chezmoi;
  2. Create a chezmoi branch to track all changes during the migration;
  3. Create a crude configuration to get the ball rolling:
    1. .chezmoiignore to handle OS- and distribution-specific exclusions;
    2. .chezmoi.toml.tmpl to declare user-, host- and user-specific configuration;
    3. run chezmoi init to create a configuration file under ~/.config/chezmoi/chezmoi.toml;
  4. Relocate files and directories according to chezmoi’s target types:
    1. rename files deployed to the home directory to:
      1. dot_<filename> for files deployed as-is;
      2. dot_<filename>.tmpl for templated files;
    2. rename directories deployed to the ~/config directory to:
      1. private_dot_config/<dirname> for directories deployed as-is;
      2. private_dot_config/private_<dirname> for directories that should not be world-readable;
    3. check the planned changes with:
      1. chezmoi status to list changed files and directories;
      2. chezmoi diff to show planned changes;
      3. chezmoi apply to apply changes to managed files and directories;
      4. chezmoi init to apply changes to the configuration file;
  5. Rinse and repeat for every OS, distribution and host ⌛

To get a rough idea, here are:

5.4. Chez moi, at last! Link to heading

If you made it this far —thank you!

chezmoi made managing my dotfiles across multiple environments much simpler, and I cannot encourage you enough to give it (or another dotfiles manager) a spin.

6. Reference Link to heading

6.1. Dotfiles management Link to heading

6.2. chezmoi dotfiles manager Link to heading

6.2.1. User configuration Link to heading

6.2.2. User Guide Link to heading

6.2.3. Reference Link to heading

6.2.4.Functions Link to heading

6.3. macOS Link to heading

6.4. Windows Link to heading