dotfile management using GNU stow

Wednesday, 26 January 2022

Sooner or later, every linux-user has to deal with keeping track of configurations (configs). These can be configs for specific software, for processes and services, or for individual environments and contexts. Keeping track of your configuration allows you to re-use a well written config, to go back to a working configuration when things go awry, and to move safely and methodically between systems.

Using git

A natural approach to managing your config is to employ some sort of a version-control system like git. A rather elegant and nifty method first (as far as I can tell) documented by Nicola Paolucci in this article, is to use bare git repositories. And this is exactly how I started, many many moons ago. While the system worked well, it was cumbersome, and involved remembering a lot of intermediate steps any time I wanted to edit a config file. Searching for an alternative, I realised that there are, in fact, a number of actively developed, open-source tools for configuration management.

Using chezmoi

In Linux, configurations are usually stored as dotfiles - i.e., files with names starting with a dot (e.g., .zshrc or .profile). Thus, most configuration management tools often have names like dotbot and dotdrop. One of the most successful and comprehensive dotfile management tools in use today is chezmoi - and after browsing through articles written by serious programmers with needs far beyond my own, this is what I moved to next. After about 6 months of use, I realised that chezmoi was exactly what it says on the tin - a no nonsense, highly specialised tool for complete and total config control. While I found chezmoi to offer more options than I will ever need, I found its interface to be demanding. It is a serious tool, and it required me to make a 24/7 commitment to config tractability - something that I am starting to believe myself incapable of. I obviously like to maintain a well documented and tractable system configuration, but the pressures of day-to-day life often end up pushing it very low down my priority list.

GNU stow

Enter GNU stow. I have been using stow for almost 3 years now, and, combined with git, it is easily the most straightforward way to deal with configuration management I have experimented with. stow is an old, old (at least older than 2001) utility used to manage “symlink farms”, i.e., it specialises in keeping track of large symbolically linked directory structures. While stow obviously has a number of important use-cases (it was not developed to manage dotfiles), its ability to seamlessly mount and unmount entire symlink hierarchies makes it an excellent tool for config management. There are many articles which describe how to use stow for this purpose, and here, I will only give a very quick and cursory overview of how I personally employ it.

1. Creating a stow

stow stores configurations in units, and each unit is stored in a separate directory. The file-structure within each unit can then be symlinked to a different base-directory in the filesystem. This is best illustrated using an example. I will use a typical scenario: configuring zsh. A simple and mostly sufficient approach is to use a ~/.zshrc file with all the aliases, environment variables and all sorts of voodoo you’d want in your terminal prompt. To stow your zsh configuration (a unit), you create a directory for it (say, ~/config/zsh) and move all your configuration (in this example, the file ~/.zshrc) inside this directory - taking care to preserve the relative directory-tree. Note that you might want to make a backup if you’re following along.

$ mkdir -p ~/config/zsh
$ cp ~/.zshrc ~/.zshrc.backup
$ mv ~/.zshrc ~/config/zsh

2. Deploying a stow

We have now created our stow for zsh in ~/config/zsh! However, if you try to open a zsh terminal, your configuration will obviously be missing. To activate (i.e., symlink) the config, the stow needs to be deployed. To deploy a stow we need to know where it is located (option -d or --dir), and which target base-directory you’d like the symlink-tree to be built on (option -t or --target). In our example, the directory where the stow is located would be ~/config, and the target directory would be ~. We can now deploy the stow we have created for zsh using:

$ stow -d ~/config -t ~ zsh

This creates a symlinked file ~/.zshrc. Invoking a zsh terminal now works as expected. What is more, since most modern editors can follow symlinks, visiting ~/.zshrc will allow you to edit your configuration stow-ed in ~/config/zsh/.zshrc.

To extend this illustration, let us suppose that you have too many aliases in your zsh config, and you store them separately in ~/.zsh/aliases (typically this would be sourced by your .zshrc). How do you stow your zsh config in this case? Remember, the idea is to simply re-create the intended relative directory-tree in your stow. So, in this case, in addition to ~/config/zsh/.zshrc, your stow would also contain the file ~/config/zsh/.zsh/aliases. The stow can be deployed as before, using

$ stow -d ~/config -t ~ zsh

It will symlink the local file-structure in ~/config/zsh atop the target directory ~ - creating two symlinked files: ~/.zshrc and ~/.zsh/aliases.

3. Multiple stows and version-control

As a final extrapolation of the example, now imagine you also want to store a configuration for direnv and perhaps another one for firefox. Following the previously outlined steps, you create the stows at ~/config/direnv and ~/config/firefox. While you don’t have to use the same base-directory (i.e., ~/config), I would recommend you do so, as it makes version-control easier. In our example, you could version-control by simply initialising a git repository in ~/config, i.e.,

$ cd ~/config && git init

You can now version-control all your stows (i.e., zsh at ~/config/zsh, direnv at ~/config/direnv, and firefox at ~/config/firefox) at once! Finally, the new stows can be deployed simultaneously using:

$ stow -d ~/config -t ~ firefox direnv

Voilà, you now know the basics of dotfile management using GNU stow!

NOTE: If you do end up using stow, you might also want to look at other options like --verbose, --simulate, --delete and --restow. And remember kids, RTFM, always.

Created using gatsbyjs, grommet, and mathjax.
Copyright © 2021 Tushar Chauhan. All rights reserved.