Managing dotfiles with GNU Stow

Posted on May 07, 2026 in Science

Everyone who works on more than one Linux machine eventually faces the dotfiles problem. You tweak your shell prompt on your laptop, add a git alias on your workstation, and within a few weeks the configurations have drifted apart. It's annoying and difficult to reconcile. I have seen people commit the entire home directory to git to address this — it has fairly obvious drawbacks.

The approach I currently use is GNU Stow, a symlink manager that was originally designed for managing software installed from source into /usr/local - or so I am told. It turns out to be an unreasonably good fit for dotfiles.

How it works

Stow operates on a simple idea: you organize files in a directory tree that mirrors the structure of your home directory, and stow creates the corresponding symlinks for you. The dotfiles repo lives at ~/.dotfiles, and inside it each "package" is a subdirectory whose contents reflect the target layout relative to $HOME. For instance, the git package contains .gitconfig, the ghostty package contains .config/ghostty/config, and so on. The directory structure inside each package is the path structure you want relative to your home directory.https://spencer.wtf/2026/02/20/cleaning-up-merged-git-branches-a-one-liner-from-the-cias-leaked-dev-docs.html

Deploying everything is a single command:

stow --verbose --target=/home/dustin/ --restow */

The --restow flag first unstows and then re-stows each package, which cleans up any stale symlinks from files you may have removed. The */ glob expands to all subdirectories — i.e., all packages. That is it. I usually dig this command out of my bash_history when I need it.

When I change a config on any machine, I edit the file in ~/.dotfiles, commit, push, and pull on the other machines. Since stow created symlinks, there is no copy step.

What I manage with it

My repo - that I keep on a git forge on my private home server - currently has around 25 packages. Some of the more interesting ones:

Terminal and shell. Ghostty terminal config, Starship prompt and Atuin for shell history sync across machines.

Git. A .gitconfig with aliases I have accumulated over the years. ciaclean anyone?

Editor configs. Neovim config and VS Code settings — including MCP server configs and custom prompt files.

Custom scripts. A bin/ package that lands a collection of small utilities into ~/bin: csv2md for quick CSV-to-markdown-table conversion, sqcat to print .sqlite files to my terminal, and some other fun ones.

AI tooling. GitHub Copilot skills (writing style guides, project management workflows, obtaining YouTube transcripts...). These live in .copilot/skills/ and get symlinked into place on every machine, so the AI assistant behaves the same way everywhere.

Systemd user services. Unit files for things like Kanshi (dynamic display configuration for Wayland), LiteLLM (a local LLM proxy), and Ollama.