How I manage my Dotfiles with GNU Stow

4 minute read Published: 2025-05-26

Anyone using a unix-like operating system probably accumulates many text configuration files as they keep using the system and installing new programs. Maybe you are like me, and use different computers that each have their own configuration and get annoyed by the fact that your config starts to drift as you use your systems. I finally got the motivation to put my configs under version control and automate installing them into my system, resulting in a neat and quick way to have the same config on all of my systems.

GNU Stow to the rescue

As is often the case, for every problem that you might encounter on unix-like systems, there probably was someone that struggled with your issue too and already solved it. Exactly that is the case with the problem of managing your dotfiles across systems. GNU Stow is a symlink farm manager, meaning that it takes files that are spread across other directories and makes it appear like they are all located in another directory. Meaning you can store your configuration and dotfiles in a central directory, put that under version control and then symlink them all into your home directory. Because they are symlinks, you can easily edit them and your changes get reflected into your repository, meaning you can commit them and sync them across your devices.

My Setup

In my case, I created a git repository where I store my files, each directory contains the files for one tool that I use. Note that in order for stow to work as intended, the directory structure in each folder needs to match the one that should be created when symlinking it. In case of starship, the shell prompt I am currently using, the structure looks like this:

starship
└── .config
    └── starship
        └── starship.toml

With this structure, stow can symlink it so that it ends up in the correct place to get picked up by my prompt:

stow -t ~ starship

This links the starship.toml that contains my settings into the correct place. Stow also allows to remove links created by it, simply add the -D flag to the command:

stow -t ~ -D starship

Automating stow

One thing that bothered me was the fact that I needed to call stow for every directory containing configuration in my repository. Since that would not scale as I put more and more config under version control, I decided that I need to automate it. I whipped up a quick script that does exactly that:

#!/bin/zsh

MODE="stow"

if [[ ($@ == "--help") || $@ == "-h" ]]; then
  echo "Installs all Directories found into ~ with GNU stow"
  echo ""
  echo "Usage: $0 [--stow|--unstow]"
  echo " --stow   Stow   (create symlinks into ~) [default]"
  echo " --unstow Unstow (remove symlinks from ~)"
  exit 0
fi


if [[ "$1" == "--unstow" ]]; then
  MODE="unstow"
elif [[ "$1" == "--stow" ]]; then
  MODE="stow"
elif [[ -n "$1" ]]; then
  echo "Unknown option: $1"
  echo "Use --help for usage."
  exit 1
fi
  
if [[ "$MODE" == "unstow" ]]; then
  STOW_CMD="-D"
else
  STOW_CMD=""
fi

find . -mindepth 1 -maxdepth 1 -type d -not -name ".git" | while read dir; do
  stow $STOW_CMD -t ~ "$(basename "$dir")"
done

Note that I am very bad at writing shell scripts, but it gets the job done. The script runs stow for every directory found and allows to control if it should create the symlinks or remove them. Surely, one could improve the script, but it works for me. I currently manage my NeoVim configuration and my shell prompt with this approach, with more soon following. Learning about stow greatly increased my comfort in syncing dotfiles across all of my devices, which makes me a happy panda.