Stow

Oct 10, 2025 12:28 ยท 1044 words ยท 5 minute read

I recently migrated my dotfiles from rcm to GNU Stow. I didn’t really have a reason, I wasn’t unhappy with rcm at all, but I saw Stow mentioned somewhere and felt like trying it out. After migrating, I still don’t have strong feelings – both tools are appropriately good and boring – but I can at least now speak to the differences and maybe that would help someone out there decide for their own dotfiles.

Philosophy ๐Ÿ”—

I worked with the rcm author, Mike Burns, at thoughtbot when he was first actively developing it back in 2014. This is how I came to know of, adopt, and contribute to it. He’s an excellent developer with strong opinions about simplicity that I very much respect. He preferred a more BSD-like approach to things vs the “bells and whistles” of GNU equivalents. For example, he liked to point out that while GNU’s implementation of /bin/true needed 100 or so lines to do nothing but exit successfully, BSD’s was just an empty file, which just so happens to have the same behavior when executed. I don’t think that’s the case any longer, but it speaks to the sort of philosophy he brings to tooling like rcm.

In contrast, stow feels very GNU. For example, it has that classic home page with links to documentation as ASCII text, PDF, Texinfo, or HTML “entirely on one web page”, carefully noting its size in bytes. It also ships a man page missing just enough details before instructing you to info stow for more.

Usage & Ergonomics ๐Ÿ”—

In keeping with the author’s preferences, rcm is built as a suite of singly-purposed POSIX sh scripts to capture (mkrc), list (lsrc), install (rcup), and remove (rcdn) dotfiles symlinks. Stow, on the other hand, is just one Perl library-as-executable (stow) with “action options” (-S, -D, and -R) to perform similar tasks. I like the rcm approach better; I also dislike that stow has no lsrc equivalent. I end up using --verbose together with --simulate to approximate the list operation, poorly.

Directory Layout ๐Ÿ”—

The tools each expect a different organization of files. And I find rcm’s directory layout more intuitive. It assumes that any top-level file x should be symlinked from $HOME/.x. There are ways to ignore files, or change the dot-prefixing behavior, but that’s the out-of-the-box default, which is well-suited to rcm’s duties.

Perhaps an example of its GNU-ness, Stow seems to be a big hammer (effectively, a package manager) used to drive a very small nail (managing dotfiles). As such, each top-level directory is considered a “package” that can be stowed. Stowing a package means symlinking the files within that package at equivalent paths in the stow directory’s direct parent. This means you need to think about that extra layer of organization, which felt unnecessary to me at first. You also have to use a --dotfiles flag, which makes any files named dot-x get linked as .x.

This all means that using stow for dotfiles requires quite a bit more care than with rcm. You have to:

  1. Place the stow directory as a direct child of $HOME (or set explicit --dir and --target)
  2. Place the actual dotfiles within some parent directory (the “package”)
  3. Name files dot- and pass --dotfiles

Here’s an example rcm layout and invocation:

~/.dotfiles/
  config/nvim/init.vim
  zshrc
% rcup
LINK ~/.config/nvim/init.vim -> ~/.dotfiles/config/nvim/init.vim
LINK ~/.zshrc -> ~/.dotfiles/zshrc

And here is the equivalent for Stow:

# Stow layout
~/.dotfiles/
  nvim/
    dot-config/nvim/init.vim
  zsh/
    dot-zshrc
% stow --dotfiles
LINK: ~/.config/nvim/init.vim -> ~/.dotfiles/nvim/dot-config/nvim/init.vim
LINK: ~/.zshrc -> ~/.dotfiles/zsh/dot-zshrc

You don’t have to break out the named packages like I do here. You could use a single “package” named dotfiles or misc, but I will admit using proper package names has grown on me. While overly verbose for the case of a single dotfile that is the same name as the package, it is nice when you can group things. I like that someone can clone my dotfiles and cohesively and easily adopt just my nvim setup, or just my Zsh and Haskell setups, etc.

Another interesting use-case is having different packages manage paths within the same location. With rcm, I had a single local/bin/ directory of scripts that was installed to ~/.local/bin. With Stow, I can organize scripts that are related to other configuration alongside it. For example, I now use git/dot-local/bin for any custom git subcommmands, and separately I have haskell/dot-local/bin for scripts used to automate tasks within Haskell projects.

rcm supports host- and tag- prefixed directories. The former are installed if $(hostname) matches and the latter are installed if the given tag is requested (by config or CLI arguments). This provides some of the same functionality as packages but it’s not as elegant or flexible.

Functionality Gaps ๐Ÿ”—

There are only two features in rcm that Stow lacks, at least that I’ve found so far: hooks/, and -c.

With rcm, you can add a conventionally-named executable to hooks/, such as pre-up, which rcm will run at the appropriate time (in this case, at the start of rcup). I used this to do things like clone down my vim plugin manager plugin before linking, or to fix some permissions after. With Stow, I believe I would just need my own script(s) that run code before or after a stow invocation. That said, I found hooks to be a double-edged sword that I actively disabled (e.g. mkrc -k) just as often as I’d expect them to run. In my experience, I only really benefited from them on the “first run” use-case, which I could solve by having my system provisioning script, which was invoking rcup and will now invoke stow, run things itself at the appropriate time.

Lastly, rcm has a -c flag, which instructs it to copy the dotfile instead of linking it. Generally speaking, this is because there may be tools that don’t like their configuration files being symlinks. I ran into this only once, when some AWS-related tool didn’t like ~/.aws/config being a symlink. I’ve since stopped using that tool, so I no longer need this behavior.

I might argue, if one were to have strong opinions about simplicity, that a tool that can’t follow a symlink to its configuration is in fact broken and we shouldn’t be adding “bells and whistles” to support them.