How I use GNU Stow to organize my dotfiles

4 mins read

#dotfiles

#linux

#ricing

Note Cover

Introduction

Over the past year, I’ve gone through multiple solutions for managing my dotfiles. From using bare repositories to tools like yadm. However, none felt efficient enough for my needs. Knowing how symlinks work, I remembered reading about using them to manage dotfiles and decided to explore this idea further. That’s how I discovered GNU stow.

Dotfiles

As an average Arch Linux enjoyer, I do have some configs laying around in my home directory, you can find my dotfiles repository on GitHub. Here’s how the repository is organized:

Terminal window
$ tree ~/.dotfiles
/home/ouassim/.dotfiles
├── alacritty
├── flameshot
├── git
├── i3
├── lazygit
├── nvim
├── picom
├── polybar
├── ranger
├── rofi
├── tmux
├── wal
├── zed
└── zsh

Each directory groups related configurations, but the structure doesn’t directly affect the output.

About GNU Stow

GNU Stow might not immediately seem like a natural fit for managing dotfiles, but it’s surprisingly effective. According to its official description:

GNU Stow is a symlink farm manager which takes distinct packages of software and/or data located in separate directories on the filesystem, and makes them appear to be installed in the same place.

In simpler terms, Stow mirrors the structure of one directory into another by creating symbolic links, making it perfect for managing a version-controlled directory of dotfiles and linking them to their appropriate locations.

How GNU Stow Works

To use Stow effectively, it’s helpful to understand these concepts:

When you “stow” a package, it creates symlinks in the target directory that point into the package.

Let’s say I have my dotfiles repository located in ~/.dotfiles. Within this repository, I have a zsh package, containing the .zshrc dotfile:

Terminal window
$ pwd
/home/ouassim/.dotfiles # <- repository
$ find zsh
zsh # <- package
zsh/.zshrc # <- dotfile

The target directory is my home directory, as this is where the symlinks need to be created. I can now stow the zsh package into the target directory like so:

Terminal window
stow --target=/home/ouassim zsh

Stow will now create appropriate symlinks of the package into the target directory:

Terminal window
$ ls -l ~/.zshrc
lrwxrwxrwx 1 ... /home/ouassim/.zshrc -> .dotfiles/zsh/.zshrc

Note that you can stow packages that contain several files or even a complex directory structure. Let’s look at the configuration for neovim which lives below ~/.config/nvim:

Terminal window
$ pwd
/home/ouassim/.dotfiles
$ find nvim
nvim
nvim/.config
nvim/.config/nvim
nvim/.config/nvim/init.lua
...

To stow the neovim package:

Terminal window
stow nvim

Verify the symlink:

Terminal window
$ ls -l ~/.config/nvim
lrwxrwxrwx 1 ... nvim -> ../.dotfiles/nvim/.config/nvim

To remove a package’s symlinks (unstow), use the -D or --delete option:

Terminal window
# unstow zsh package
stow -D zsh

This removes the symlink:

Terminal window
$ ls -l ~/.zshrc
ls: cannot access '/home/ouassim/.zshrc': No such file or directory

Ignoring files and directories

Stow, by default, ignores certain files and directories, as defined in its built-in ignore list. You can customize this behavior by adding a .stow-local-ignore file in your stow directory.

The default ignore file includes:

# Comments and blank lines are allowed.
RCS
.+,v
CVS
\.\#.+ # CVS conflict files / emacs lock files
\.cvsignore
\.svn
_darcs
\.hg
\.git
\.gitignore
\.gitmodules
.+~ # emacs backup files
\#.*\# # emacs autosave files
^/README.*
^/LICENSE.*
^/COPYING

Automating with a Makefile

To simplify the process, I use a Makefile in my dotfiles repository:

Terminal window
all:
stow --verbose --target=$$HOME --restow */
delete:
stow --verbose --target=$$HOME --delete */

The --restow flag ensures outdated symlinks are removed before creating new ones. Updating or cleaning up my dotfiles has become as simple as:

Terminal window
make # Update symlinks
make delete # Remove all symlinks

Version Controlling Your Dotfiles

Using Git to track your dotfiles helps keep changes organized and enables easy synchronization across machines:

Terminal window
git init
git add .
git commit -m "storing initial dotfiles"

Add a .gitignore to exclude unwanted files and a README.md to document your setup.

Conclusion

I hope this has helped you understand better how to manage your dotfiles. While this method may not be for everyone, it’s my preferred approach.

You can check out my dotfiles repository here. It’s a work in progress, but it might give you some inspiration.

Thanks for reading, PEACE ✌️.