Managing Dotfiles with GNU Stow
The goal: keep every configuration file in a single, Git-versioned repository, and put each one where the system expects it with symlinks instead of copies. GNU Stow does the symlinking. Thanks to Paulo (PauloHFS), who pointed me to Stow — I researched and implemented the setup below.
My environment for the examples: Arch Linux (Omarchy) with Hyprland, but the technique is OS-agnostic.
What Stow does
Stow is a symlink farm manager. Instead of copying config files into places like
~/.config, you keep them organized in one folder (e.g. ~/dotfiles) and Stow creates the
symlinks in the right locations, pointing back at the repository.
Why this is worth it:
- Centralization — every important file lives in one Git-versioned folder.
- Modularity — enable or disable one program's config at a time (
stow nvim) without touching the rest. - Simplicity — no scripts to move files around; Stow is the whole mechanism.
Install
On Arch Linux:
sudo pacman -S stow
Repository structure
Each top-level folder is a package, and its contents mirror the structure starting from
$HOME:
~/dotfiles/
├── hyprland/ <-- package name
│ └── .config/
│ └── hypr/
│ └── hyprland.conf
├── waybar/
│ └── .config/
│ └── waybar/
│ └── config.jsonc
└── bash/
└── .bashrc <-- lands directly in $HOME
Usage
Apply a package (create its symlinks):
cd ~/dotfiles
stow hyprland
That creates ~/.config/hypr/hyprland.conf as a link pointing into the repository. Remove
the links again with:
stow -D hyprland
Resolving conflicts on a fresh machine
A freshly installed system already ships default config files, and Stow refuses to
overwrite real files (to avoid data loss), so stow errors out when a target exists. Two
ways to resolve it:
1. The "git trick" (best for migrations). Let the repository win, then restore its original contents:
stow --adopt * # WARNING: this overwrites the repo's files with the machine's current ones
git restore . # bring the repo's versions back; $HOME now reflects the repository
Because Stow turned the $HOME files into links, restoring the repo makes $HOME
immediately reflect what's in Git.
2. Manual cleanup (more cautious). Delete the originals first, then stow:
rm ~/.bashrc
rm -rf ~/.config/hypr
cd ~/dotfiles && stow *
Per-machine variants with Git branches
Different machines need slightly different configs — a desktop has monitor layouts a laptop doesn't, and so on. I keep that variation in branches rather than in conditionals:
main— desktop (monitor setup and related tweaks).- a separate branch (e.g.
notebook) — the laptop variant.
On each machine you check out the branch that matches it, then stow. Shared config stays common; only the machine-specific parts diverge, and Git makes the difference explicit and reviewable.
Tips learned the hard way
-
Avoid absolute symlinks inside the repo (links pointing at
/home/<user>/...). They break on machines where the username differs. If you hitabsolute symlinkerrors, delete the link in the repo and keep a real file or a relative link. -
Watch for config version drift when moving between machines — a program may rename a parameter between versions. Validate after stowing; for Hyprland, for example:
hyprctl configerrors