Managing my dotfiles with GNU Stow

Wesley Schwengle
6 min readApr 8, 2019

--

Dotfiles, every Linux/Unix user has them. I’ve had mine on FreeBSD, OSX/MacOS, Solaris, RedHat, Ubuntu and Debian machines and probably on other machines as well. They date back to at least October 4th 2009 when I made the switch from CVS to Git, but they are older than that. I just tarballed them before that and untarred. Or just simple scp/rsync across servers. Because I’ve moved my git scripts to a separate repository in order to freely share them (see My git workflow explained) I got an itch to also open source my dotfiles. Or Dotty as I like to call this project.

Since the inception of my dotfile directory I haven’t really looked at the way I manage or maintain them. I just updated the custom build installation script as it did its job. Then there was the day my colleague asked me to share my git scripts and aliases. I moved all the scripts and aliases away to Bum. My dotfiles felt abandoned, they needed some loving too.

So I went looking for a tool to manage my dotfiles. There are plenty of tools that do the job. I looked at them and they weren’t my cup of joe. And then there was GNU Stow. The best thing about Stow is, it isn’t really written for dotfile management. It tries to solve another problem, installing software in place to make sure once you install it you can remove it safely from your machine and not have to worry about “forgotten” files. Managing dotfiles is just a nice perk to have.

How does Stow work?

Well, Stow is a glorified symlink farm manager. It manages symlinks from A to B. It is a tool made for system administrators that need to maintain software on machine and may need to add/remove software packages. In order to do so you need to be able to remove software and all of the files associated with them. You could do an installation directly into.. let’s say /usr/local, but then you would need to make sure you know about every little file and where it is installed: painful! Some projects allow you to do make uninstallbut not all do this. And you would need to keep that Makefile in order to remove a given package. You could also go for the option to install the package in /usr/local/<packagename>and then educate your users to add that path in
their PATH, also sub optimal. This is where Stow shines. You enter /usr/local and then do stow <packagename>. Now every thing gets symlinked in /usr/local, <packagename/bin goes to /usr/local/bin, <packagename>/libto /usr/local/lib, you get the point.
Removing a package would start with stow -D <packagename> and Stow
will happily remove all the symlinks and you can rm -rf /usr/local/<packagename>to remove the whole lot. System administration made easy. You may think that this problem may be a thing of the past with all the package managers Linux distro’s currently offer (apt, yum, apk, ports). But there are still use cases for Stow, you don’t always install from your package manager. For example, if you build zsh from their git repo and want to try it out on your box, you can install it into /usr/local/zshand you then stow zsh to /usr/local. Easy does it.

Dotfiles

Back to the dotfiles. My dotfiles are in my .shell directory in my home dir and I did a thing similar to stow, but only without the de-installation process and not being open source. I reorganized my .shell dir to be Stow compatible. I now have two shell directories. One is called .dotty-private, which isn’t for you and the other is called .dotty, which I share with the world.

Dotty

In this repo I have everything I need to setup a home dir to my liking. It includes application configuration (MPlayer, Vim, Git, etc), (bin-)scripts, my zsh configuration, Debian related things, etc. I just hit setup-env.shand it rolls out all the things I need. It basicaly does stow stow zsh vim script apps debian perl git. I’ve also included a cpanfilefor all of the Perl scripts that are found in the bin-dir and a Debian package list for all of the tools that I want to use. It feels much more organized than my home brew solution.

So how does this work? Well, this is my file tree:

$ tree -a -d -L 2 -I .git
.
├── apps
│ ├── .config
│ ├── .conky
│ ├── .local
│ ├── .mplayer
│ ├── .ncftp
│ ├── .siege
│ ├── .texmf-var
│ └── .wireshark
├── awesome
│ └── .config
├── debian
├── git
│ └── .config
├── i3
│ └── .config
├── perl
│ └── .dzil
├── scripts
│ └── bin
├── stow
├── vim
│ └── .vim
└── zsh
└── .zsh

Each important thing that I use has its own directory which then has a similar layout as they would have in my homedir. Now if I stow zsh, it will put all the files found in zsh into my homedir:

zsh
├── .Xdefaults
├── .aliasrc
├── .functions
├── .profile
├── .shellrc
├── .zlogin
├── .zlogout
├── .zprofile
├── .zsh
│ ├── .zcompdump
│ ├── autoload
│ ├── autoloadrc
│ ├── cache
│ ├── completion
│ ├── endzshrc
│ ├── keybindrc
│ ├── minimal-zshrc
│ ├── nameddirrc
│ ├── optionsrc
│ ├── prompt
│ ├── promptrc
├── .zshenv
└── .zshrc

It won’t create a symlink for everything in the .zsh directory, stow will just symlink .zsh to .dotty/zsh/.zsh. If however I would have another stow dir that provides files into.zsh it will then symlink all the files in .zsh and add new symlinks to the other stow directory. In Stow lingo the former is called a foldable directory and the latter is unfolded.

The secret Dotty

In my secret dotty are all the things I don’t want to share with the world. It contains my .ssh and .gnupgand some other things I just don’t want you to know about. Because OpenSSH doesn’t allow for symlinks from within the .sshdirectory I had to move over all the files to this secret dotty repo. I do however don’t want my private and public keys to be in my git repo, so I added them to my .gitignorefile. The only things that I commit are my authorized_key and my ssh config file. For GnuPG most files are ignored as well, except for the config files.

Now I go into .dotty-privateand hit stow gnupg sshand my setup
just roles out automatically.

Stowing container

I can test all the things in a Docker container if I want to, as both repositories have a Dockerfile and a docker-compose file. They use the base image of my Stowing project. This project is GPL licensed because Stow is GPLv2 and I publish the image, but if you don’t publish the image you can fork my dotfiles and redistribute them under the MIT license as I don’t ship stow within the repository. All the legal stuff aside. You can try it out by just running

docker run -ti \
-v/path/to/allyourdotfiles:/root/stow \
registry.gitlab.com/waterkip/stowing:latest zsh

Or you can create a Dockerfile with the following contents in your dotfiles repository:

FROM registry.gitlab.com/waterkip/stowing:latestCOPY . .

Now you can build and run it:

docker build -t dotfiles .
docker run -ti dotfiles zsh # or bash if you are a bash user

You get to see your dotfile directory in /root/stow and can now see what/when happens when you have a go at stow.

Pros

There is very little configuration on your part, just enter a directory and Stow will do its job. A very strong plus in my book. Truth be told, Stow manages itself within my repository, I have a .stow-global-ignore file that deals with the files I want stow to ignore (mostly Docker and Git files). I’m considering opening an pull request to add these to the default set of ignored files so I don’t have to manage this.
A personal pro, it is written in Perl. As a Perl developer this is a big plus, others may not share this viewpoint.
As said earlier, Stow isn’t just for dotfile management, it is used to solve another problem. Having Stow means you can maintain your custom build software in the same way you can maintain your dotfiles. I do this for example with Zsh , Git and the Lastpass CLI client.

Cons

All the yays aside, there are some limitations with Stow. You need to have the same directory layout for each stow directory. For example, i3 stores its configuration in .config/i3/config . Your i3 directory needs to have the same layout: i3/.config/i3 . Now stow i3 will place the configuration in the correct directory. The dotfiles themselves need to be .dotfiles, there is a PR merged into Stow that deals with this problem, but it isn’t released yet in a tagged version. Once this is done, you can call your .zshrc dot_zshrc. Much nicer than to go into a directory and it looks seemingly empty.
You can’t have a stow directory somewhere else. I can’t get my ~/code/bum project to be stowed into my home dir. This might very well be PEBKAC and is a minor annoyance. I already solved this via other means in my pre-Stow era.

You can find my dotfiles at https://gitlab.com/waterkip/dotty

--

--