Your dotfiles are your workstation DNA: shell behavior, editor defaults, Git identity, terminal workflow. When those files drift across machines, friction shows up everywhere.
GNU Stow gives you a simple way to keep that setup clean, versioned, and repeatable, without fragile scripts or manual symlink juggling.
Why Dotfiles Quickly Become Hard to Manage#
Copying a .bashrc or .gitconfig sounds easy until real life gets involved:
- one machine has the latest tweaks
- another still has a local change you forgot about
- a hand-made symlink breaks after a directory move
Before long, you are not sure which version is canonical, and every new laptop starts with avoidable setup debt.
GNU Stow in One Sentence#
GNU Stow is a symlink manager. You organize your configs in directories, then Stow automatically creates symbolic links in the right place under your $HOME.
Conceptual example:
~/dotfiles/ $HOME/
├── bash/
│ └── .bashrc -----> ~/.bashrc
├── git/
│ └── .gitconfig -----> ~/.gitconfig
└── nvim/
└── .config/
└── nvim/
└── init.lua ----> ~/.config/nvim/init.luaInstallation#
# Debian/Ubuntu
sudo apt install stow
# Arch Linux
sudo pacman -S stow
# Fedora
sudo dnf install stow
# macOS
brew install stowA Dotfiles Repository Layout That Stays Manageable#
One directory per tool, so you can enable or disable things selectively.
~/dotfiles/
├── bash/
│ └── .bashrc
├── git/
│ ├── .gitconfig
│ └── .gitignore_global
├── nvim/
│ └── .config/nvim/init.lua
├── tmux/
│ └── .tmux.conf
└── zsh/
└── .zshrcInitial Setup (One Time)#
# 1) Create the repository
mkdir -p ~/dotfiles
cd ~/dotfiles
git init
# 2) Move an existing config into its package directory
mkdir -p bash
mv ~/.bashrc bash/
# 3) Let Stow create the symlink
stow bash
# 4) Version it with Git
git add .
git commit -m "Add bash config"Everyday Workflow: Simple and Reliable#
After the initial setup, your day-to-day loop is straightforward.
- Edit a file in
~/dotfiles/... - Test the change locally
- Commit + push
- Pull on other machines
- Re-run
stowwhen needed
Typical flow:
cd ~/dotfiles
# Edit your config
$EDITOR zsh/.zshrc
# Preview link changes without modifying anything
stow -n -v zsh
# Apply
stow zsh
# Version with Git
git add zsh/.zshrc
git commit -m "Tune zsh aliases"
git pushNew Machine: The Real Productivity Gain#
Bootstrapping a new environment becomes predictable:
git clone git@github.com:you/dotfiles.git ~/dotfiles
cd ~/dotfiles
stow bash git nvim tmux zshThat is the point: no mystery steps, no checklist archaeology, just your environment back in place.
Pre-Existing Dotfiles: Safe Conflict Resolution#
If target files already exist in your $HOME, Stow will stop with a conflict. Use one of these two patterns.
Option 1: Backup first (safest)#
cd ~/dotfiles
# Preview what Stow wants to link
stow -n -v bash git nvim tmux zsh
# Backup existing files before applying links
backup_dir="$HOME/.dotfiles-backup/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$backup_dir"
for f in ~/.bashrc ~/.gitconfig ~/.tmux.conf ~/.zshrc ~/.config/nvim; do
[ -e "$f" ] && mv "$f" "$backup_dir/"
done
# Apply links after conflicts are removed
stow bash git nvim tmux zshOption 2: Controlled adopt workflow#
The --adopt flag tells Stow to move an existing file from $HOME into your package tree, then create a symlink pointing to it. This operation is destructive to your versioned config: it replaces the file in your repository with whatever currently lives in $HOME.
cd ~/dotfiles
# Dry-run first
stow -n -v --adopt zsh
# Adopt only one package at a time
stow -v --adopt zsh
# Review and version the adopted changes
git diff
git add zsh/.zshrc
git commit -m "Adopt existing zsh config"--adopt will silently overwrite the file in your Stow package directory with the file from $HOME. If $HOME has an older or corrupted version, that’s what ends up in your repository. Once committed, you’d need to restore from history.
Practical rule: start with backup-first on new machines or unknown states, and reserve --adopt for cases where you explicitly want to import an existing local config into the repo—and only after reviewing the dry-run output.
Multi-Machine Management (Linux, macOS, Work/Personal)#
You can separate shared and machine-specific configurations:
dotfiles/
├── common/ # shared across all machines
│ ├── git/
│ │ └── .gitconfig
│ └── nvim/
│ └── .config/nvim/
│ └── init.lua
├── linux/ # Linux-only configs
│ ├── bash/
│ │ └── .bashrc
│ └── tmux/
│ └── .tmux.conf
└── macos/ # macOS-only configs
└── zsh/
└── .zshrcThen enable only what matches the current machine:
# On Linux
cd ~/dotfiles
stow common/linux
# On macOS
cd ~/dotfiles
stow common/macosKey principle: every directory you run stow on must contain files that map directly to their target location in $HOME. If a package contains .bashrc, Stow will try to link it to ~/.bashrc. Plan your directory names accordingly to avoid accidental overwrites.
Avoid Common Mistakes#
- Use
stow -n -v <package>before important changes. - Keep the tree readable: one package = one tool.
- Keep temporary, host-specific files out of versioned packages.
- Check symlinks when in doubt:
ls -l ~/.bashrcSecurity: What You Should Never Version#
Never commit:
- tokens and API keys
- private SSH keys
- passwords
Example minimal protections:
*.local
.env
.ssh/Practical approach:
- use environment variables for secrets
- keep local non-versioned files (
.gitconfig.local) - add a pre-commit hook or a secrets scanner
Even in a personal repository, assume it could become public one day. Treat secrets like production data.
TL;DR#
GNU Stow gives you exactly what dotfiles need:
- simple: built on UNIX symlinks
- versioned: standard Git repository
- reproducible: fast migration to a new machine
- modular: selective package activation
If you know how to use ln -s, you already know how to use Stow.
