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
andC:\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:
- create a Git repository, aptly named
dotfiles
; - clone this repository under a well-known location, e.g.
~/.dotfiles
or~/.dots
; - add all dotfiles to the repository;
- wait, did we say “caches, databases, sensitive information”?
- add a top-level
.gitignore
file to ignore editor settings and swap files; - add directory-specific
.gitignore
files to ignore undesired resources;
- add a top-level
- 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
fromnano
toneovim
? or our$PAGER
frommore
tobat
?
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:
- Clone my dotfiles repository to
~/.local/share/chezmoi
; - Create a
chezmoi
branch to track all changes during the migration; - Create a crude configuration to get the ball rolling:
.chezmoiignore
to handle OS- and distribution-specific exclusions;.chezmoi.toml.tmpl
to declare user-, host- and user-specific configuration;- run
chezmoi init
to create a configuration file under~/.config/chezmoi/chezmoi.toml
;
- Relocate files and directories according to
chezmoi
’s target types:- rename files deployed to the home directory to:
dot_<filename>
for files deployed as-is;dot_<filename>.tmpl
for templated files;
- rename directories deployed to the
~/config
directory to:private_dot_config/<dirname>
for directories deployed as-is;private_dot_config/private_<dirname>
for directories that should not be world-readable;
- check the planned changes with:
chezmoi status
to list changed files and directories;chezmoi diff
to show planned changes;chezmoi apply
to apply changes to managed files and directories;chezmoi init
to apply changes to the configuration file;
- rename files deployed to the home directory to:
- Rinse and repeat for every OS, distribution and host ⌛
To get a rough idea, here are:
- the dotfiles before migrating to
chezmoi
- the first commit of the migration
- the dotfiles after migrating to
chezmoi
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
- Hidden file and hidden directory (Wikipedia)
- Dotfiles (Arch Wiki)
- Dotfiles Management (Mitxela), 2022-12-19
- webpro/awesome-dotfiles
- Dotfiles and dev tools provisioned by Ansible (Alex Palcuie), 2014-06-22
6.2. chezmoi
dotfiles manager
Link to heading
6.2.1. User configuration Link to heading
chezmoi
Github topic- How To Manage Dotfiles With Chezmoi (Jerry Ng), 2022-02-14
- Managing dotfiles with chezmoi (Nate Landau), 2025-01-19
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
- Automating the Setup of a New Mac with All Your Apps, Preferences, and Development Tools (Moncef Belyamani), 2024-01-12
6.4. Windows Link to heading
- PowerShell/PSReadLine - A bash inspired readline implementation for PowerShell
- JanDeDobbeleer/oh-my-posh - Cross platform/shell prompt renderer