How to Manage Dotfiles with GNU Stow
Use GNU Stow to turn a Git repository into a symlink farm for your dotfiles — deploy any config to a new Linux machine in under a minute.
Before you start
- ▸Git installed and a basic understanding of git add/commit/push
- ▸A remote Git host account (GitHub, GitLab, Codeberg, or self-hosted)
- ▸Familiarity with navigating the terminal and editing files in $HOME
- ▸GNU Stow 2.3 or later (check with stow --version)
Dotfiles are the personality of your Linux setup — your shell aliases, editor config, terminal colors, and window manager rules all live in hidden files scattered across $HOME. The problem is tracking them. Copy them manually and they drift out of sync. Symlink them by hand and you forget which links point where. GNU Stow solves this cleanly: it turns a plain directory tree into a symlink farm, letting you keep every config file in a single Git repository and deploy it to any machine in seconds.
What GNU Stow Does
Stow is a symlink manager, nothing more. You put your dotfiles inside a stow package directory, run stow, and it creates symlinks in a target directory (by default, the parent of the package directory) that mirror the package's layout. It does no copying, no templating, no encryption — just symlinks. That simplicity is its strength.
The mental model: your repo lives at ~/.dotfiles/. Inside it, each subdirectory is a package — bash, nvim, tmux, etc. The files inside those packages are laid out exactly as they should appear relative to $HOME. Stow creates the symlinks; Git tracks the real files.
Install GNU Stow
Stow is in every major distribution's repositories.
Debian / Ubuntu:
sudo apt install stow
Fedora / RHEL / Rocky Linux:
sudo dnf install stow
Arch Linux:
sudo pacman -S stow
Create the Dotfiles Repository
Step 1: Initialize the directory and Git repo
Pick a location. ~/.dotfiles is conventional and keeps it hidden from casual ls output.
mkdir -p ~/.dotfiles
cd ~/.dotfiles
git init
Step 2: Understand the directory layout
Each immediate subdirectory of ~/.dotfiles is a Stow package. Inside each package, recreate the path from $HOME to the config file. For example, if you want to manage ~/.bashrc and ~/.config/nvim/init.lua, the layout looks like this:
~/.dotfiles/
├── bash/
│ └── .bashrc
└── nvim/
└── .config/
└── nvim/
└── init.lua
When you stow the bash package from inside ~/.dotfiles, Stow creates ~/.bashrc → ~/.dotfiles/bash/.bashrc. When you stow nvim, it creates ~/.config/nvim/init.lua → ~/.dotfiles/nvim/.config/nvim/init.lua.
Migrate Existing Dotfiles into the Repo
Step 3: Move files and create package directories
The key rule: move the real file into the repo, then let Stow put the symlink back. Never copy — the file in the repo must be the canonical version.
# Create the bash package and move .bashrc into it
mkdir -p ~/.dotfiles/bash
mv ~/.bashrc ~/.dotfiles/bash/.bashrc
# Create the nvim package preserving the subdirectory path
mkdir -p ~/.dotfiles/nvim/.config/nvim
mv ~/.config/nvim/init.lua ~/.dotfiles/nvim/.config/nvim/init.lua
If the original file doesn't exist yet (fresh machine), just create the directory structure and add the file directly inside the repo.
Step 4: Stow the packages
Always run stow from inside ~/.dotfiles, targeting $HOME explicitly with -t to avoid surprises.
cd ~/.dotfiles
stow -t ~ bash
stow -t ~ nvim
To stow everything at once, pass all package names or use a loop:
cd ~/.dotfiles
for pkg in */; do stow -t ~ "${pkg%/}"; done
Step 5: Verify the symlinks
ls -la ~ | grep dotfiles
ls -la ~/.config/nvim/
You should see output similar to (exact paths will vary):
.bashrc -> .dotfiles/bash/.bashrc
init.lua -> /home/user/.dotfiles/nvim/.config/nvim/init.lua
Commit and Push to Git
Step 6: Add a .gitignore and commit
Exclude anything sensitive or machine-local you don't want committed.
cat > ~/.dotfiles/.gitignore <<'EOF'
.DS_Store
*.swp
*.secret
EOF
git -C ~/.dotfiles add -A
git -C ~/.dotfiles commit -m "Initial dotfiles"
# Add your remote and push
git -C ~/.dotfiles remote add origin [email protected]:yourname/dotfiles.git
git -C ~/.dotfiles push -u origin main
Bootstrap a New Machine
Step 7: Clone and stow on a fresh system
On a new machine, install Stow, clone your repo, then stow each package. Because the target files don't exist yet, Stow creates fresh symlinks with no conflicts.
git clone [email protected]:yourname/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
for pkg in */; do stow -t ~ "${pkg%/}"; done
If a config file already exists on the new machine (e.g., a default .bashrc placed by the distro), Stow will refuse to overwrite it and print a warning. Back up and remove the conflicting file first, then re-run stow.
Day-to-Day Workflow
Adding a new config file is three steps: create the package directory structure, move the file in, stow it.
# Example: add tmux config
mkdir -p ~/.dotfiles/tmux
mv ~/.tmux.conf ~/.dotfiles/tmux/.tmux.conf
stow -t ~ tmux
git -C ~/.dotfiles add tmux/.tmux.conf
git -C ~/.dotfiles commit -m "Add tmux config"
To remove a package's symlinks without deleting the files in the repo, use -D:
stow -t ~ -D tmux
To restow (remove then re-create symlinks, useful after restructuring), use -R:
stow -t ~ -R nvim
Handling Machine-Specific Configs
Some files differ between machines — your work laptop might need a different Git user email than your home desktop. Two clean approaches:
- Local override files: Keep a base
~/.gitconfigin the repo and use Git'sincludeIfdirective to pull in a machine-local file that Stow doesn't manage (and that you add to.gitignore). - Per-machine packages: Create packages named
work-bashorhome-nvimand only stow them on the relevant machine. A short bootstrap script can handle this automatically based on hostname.
# Bootstrap snippet: stow common packages plus machine-specific ones
HOST=$(hostname -s)
COMMON_PKGS=(bash nvim tmux git)
for pkg in "${COMMON_PKGS[@]}"; do
stow -t ~ "$pkg"
done
[[ -d "$HOST" ]] && stow -t ~ "$HOST"
Troubleshooting
Stow reports a conflict
If Stow says a file already exists and is not a symlink to the repo, the real file is in the way. Back it up and remove it:
mv ~/.bashrc ~/.bashrc.bak
stow -t ~ bash
Symlinks point to wrong relative paths
Always run Stow from inside the dotfiles directory or use -d ~/.dotfiles -t ~ explicitly. Running it from the wrong directory produces broken relative symlinks.
stow -d ~/.dotfiles -t ~ bash
Stow folding causes unexpected behavior
Stow uses "tree folding": if an entire directory can be represented by a single symlink, it will symlink the directory rather than individual files inside it. This can surprise you when another tool writes a new file inside what it thinks is a real directory but is actually a symlink. Use --no-folding to force per-file symlinks:
stow -t ~ --no-folding nvimFrequently asked questions
- What happens if a dotfile already exists on the target machine when I run stow?
- Stow refuses to create a symlink if a real file already exists at that path and prints a conflict warning. Back up and remove the existing file, then re-run stow.
- Can I use Stow with XDG config files inside ~/.config?
- Yes. Recreate the subdirectory path inside your package — for example, nvim/.config/nvim/init.lua — and Stow will create ~/.config/nvim/init.lua as a symlink. You may need to create the ~/.config directory first if it doesn't exist.
- Is it safe to store dotfiles on a public GitHub repository?
- Only if you audit them carefully. Never commit API keys, SSH private keys, or passwords. Use a .gitignore to exclude sensitive files, and consider a private repo or tools like git-crypt for anything that must be encrypted.
- What is Stow's tree-folding behavior and when should I disable it?
- Stow may symlink an entire directory instead of individual files inside it when the whole directory belongs to one package. This breaks if another program writes new files into that directory expecting it to be real. Use --no-folding to force Stow to create individual file symlinks instead.
- Do I need to re-run stow after editing a managed config file?
- No. Because the target is a symlink, any edits you make via the symlink path go directly into the file inside your repo. Just git add and git commit when you are ready to save the change.
Related guides
Linux Clipboards Explained (+ Clipboard Managers)
Learn the difference between Linux's PRIMARY and CLIPBOARD selections, use xclip, xsel, and wl-clipboard from the terminal, and manage history with GPaste or Klipper.
Configure LibreOffice for Daily Use
Configure LibreOffice for daily use: set default save formats for MS Office interop, tune autosave, install fonts, and add productivity extensions.
Configure the Touchpad and Multitouch Gestures
Configure Linux touchpad behavior and multitouch gestures using libinput, libinput-gestures, and native GNOME and KDE Plasma settings on both Wayland and X11.
Wayland vs X11: How to Choose and Configure Each
Know when to run Wayland or X11, how to check your current session, switch at login with GDM/SDDM/LightDM, and handle NVIDIA and XWayland edge cases.