- GNU Stow allows you to centralize dotfiles in a single repository and link them to the system using symlinks in a clean and reversible way.
- There are two main organizational models: unified repository and package-based approach, with different levels of modularity.
- Combining Stow with Git makes it easy to version, back up, and replicate configurations across multiple machines with just a few commands.
- Good practices such as using .stow-local-ignore, respecting the directory structure, and avoiding mixing real files and symlinks ensure a robust workflow.
If you use Linux , macOS, or Termux on Android daily, sooner or later you end up accumulating a bunch of configuration files scattered throughout your home directory: .zshrc, .bashrc, .config, nvim, Hyprland, etc. When you only have one computer, you can manage, but as soon as you work with multiple computers or servers, keeping all this organized becomes a whole different story.
This is where GNU Stow comes in , a symbolic link manager that has become one of the cleanest, simplest, and most reversible ways to manage dotfiles. It's not the only possible approach (alternatives exist such as Git's bare repositories, yadm, Chezmoi, Dotbot, etc.), but its minimalist philosophy is a perfect fit if you want something powerful without too much fuss behind the scenes.
What exactly is GNU Stow and why is it useful for managing .dotfiles?
GNU Stow is originally a "symlink farm manager" : a tool designed to organize multiple "packages" of files in one place and expose them in another directory using symbolic links. Although it was conceived to manage local software installations, the community quickly adopted it for handling dotfiles because it fits the role perfectly.
The basic idea is very simple: you store all your configurations in a single central repository (for example, ~/dotfiles) , structured as if they were going to live in your $HOME directory, and Stow takes care of creating symlinks from your home directory to those files. This allows you to version them with Git, clone them on multiple machines, and reproduce your environment with one or a few commands.
Important note: Stow is not a "dotfile tool" in the strictest sense ; it doesn't store its own state, maintain a database, use templates, or employ encryption. It simply creates and deletes symbolic links following a directory structure. This is precisely why it's so easy to understand and revert.
This minimalist philosophy contrasts with more complex solutions like Chezmoi, which add templates, secret management, password manager integration, and a more automated workflow. With Stow, you're in charge: everything is in the file system and your Git repository , without any intermediate layers.
Advantages of using Stow compared to other ways of managing dotfiles
Before Stow became popular, many people managed their dotfiles using "cp" and "mv" : they would manually copy configurations between computers or maintain a repository that required constant updating. It was easy to end up with multiple versions of the same file and not know which one was actually in use.
With Stow, all the "real" files live in your dotfiles directory, and your $HOME directory only contains symbolic links . This means that when you edit, for example, ~/.zshrc, you're actually modifying the file inside your repository. There are no duplicates, no desynchronization, and no need to remember what to copy and where.
Another clear advantage is reversibility: if you want to undo what Stow has done, simply run “stow -D package” from your dotfiles folder and it will remove all the symbolic links created for that package. It doesn't delete your actual configurations (which remain in the repository), it only cleans up the symbolic links to the destination.
Furthermore, Stow works seamlessly with Git: you can version ~/dotfiles like a regular repository , make commits, create branches, use GitHub or GitLab for backups, and more. Stow automatically ignores the .git folder when creating links, so you don't risk filling your $HOME directory with internal Git files.
Finally, unlike other heavier tools, Stow is usually available on any Linux distro and even on macOS via Homebrew ; it is basically a Perl script with very few dependencies and works in any UNIX -like environment.
Common alternatives: Git bare, yadm, Chezmoi, Dotbot…
When you consider managing dotfiles seriously, the same list of options usually appears: a bare Git repository, yadm, Dotbot, Chezmoi, and Stow . Each approach has its own style and audience, so it's worth placing Stow within that ecosystem.
The bare Git repository method involves initializing a repository without an associated working tree and using Git aliases so that $HOME itself acts as the working directory. Advantages: there are no symbolic links, Git works directly on your actual files, and the command flow is very simple. Many users comment that they were surprised by how easy it was to follow a "DT"-style tutorial and have it working without touching any symlinks.
On the other hand, Chezmoi focuses entirely on advanced dotfile management : templates for handling differences between machines, integration with password managers, file encryption with GPG or AGE, hooks for running scripts during installation, robust cross-platform support, and more. It's ideal if you need to manage secrets, support many different systems, or automate complex installations.
Stow is at the opposite extreme: it knows nothing about secrets, templates, or scripts . It simply creates clean symlinks. For many users, this is a virtue: less to learn, less "magical" behavior, and greater transparency. If you need heavy conditional logic, Chezmoi is probably a better fit; if you just want to keep your configurations organized without complications, Stow is a very reliable classic.
There are also tools like Yadm or Dotbot that automate many tasks (including running post-installation scripts, cloning repositories, installing packages, etc.). Even so, many developers still prefer Stow because it's easy to audit, integrates well with existing Git workflows, and adapts seamlessly to both minimalist setups and more demanding desktop environments.
Organizational approaches: unified repository vs package-based repository
When you start using Stow, one of the first decisions you have to make is how to structure your dotfile repository . Broadly speaking, there are two popular patterns: the unified approach and the package-based approach.
In the unified model, your dotfiles repository looks practically the same as your home directory: files like .bashrc or .zshrc in the root, and folders like .config/nvim or .config/lazygit inside . Something like this:
dotfiles-unified/
├── .bash_aliases
├── .bash_completion/
│ └── alacritty.bash
├── .bashrc
└── .config/
├── lazygit/config.yml
└── nvim/…
With this design, you enter the repo folder, run `stow.` , and suddenly all your configurations are linked to your `$HOME` directory . It's incredibly convenient when you want to clone your entire environment to a new machine with a single command and don't need much differentiation between machines.
The package-based approach works differently: you create a subdirectory for each "module" or application . For example, one for bash, one for nvim, one for lazygit, one for zsh, one for Hyprland, and so on. Each directory contains the files with the full path they would have in your $HOME directory. Something like:
dotfiles-packages/
├── bash/
│ ├── .bash_aliases
│ ├── .bash_completion/alacritty.bash
│ └── .bashrc
├── lazygit/.config/lazygit/config.yml
└── nvim/.config/nvim/…
With this scheme, you can decide which packages to "activate" on each machine: on one machine you run "stow bash nvim lazygit", on another perhaps "stow zsh nvim" . This is very useful when working with multiple distributions (for example, Arch on one PC and Fedora on another) or with different shells (fish on one machine, bash on another) and you want to keep everything in a single repository, but choose what to apply in each environment.
The catch? It's a bit more complex: you can no longer just use `stow.` and be done with it; you have to remember which packages you need , or create a small script for each machine that calls `stow` with the correct combination. Even so, many users prefer this fine-grained control, especially if they have very specific software on each machine.
How Stow works internally: the concept of directory “mirroring”
The key to understanding Stow is its directory mirroring system . Stow doesn't guess paths; it simply looks at how the files are organized within the "package" and places the corresponding symlinks in the destination directory.
For example, if an application expects its configuration in:
~/.config/ghostty/
Your module inside ~/dotfiles should have exactly that relative path:
~/dotfiles/ghostty/.config/ghostty/
Anything you place inside (for example, a file called config) will be linked by Stow to the correct location. This way, Ghostty will continue to read its configuration from ~/.config/ghostty/config, but that file will actually point to the one stored in your repository.
This pattern is repeated for any tool: Waybar would have something like ~/dotfiles/waybar/.config/waybar/, Neovim ~/dotfiles/nvim/.config/nvim/ , and so on. The process is extremely uniform, making scaling to more programs almost mechanical.
For dotfiles that live directly in $HOME (such as ~/.gitconfig or ~/.zshrc) the logic is identical: within the git package you will have a .gitconfig file in the root , and Stow will take care of creating the link in your home directory when you run “stow git”.
Step by step: setting up a dotfiles repository with Stow
The typical workflow with Stow is simple and can be summarized in a few well-defined steps, both on Linux and macOS. The important thing is to get used to the fact that the "real" files are always inside the repository and not scattered throughout your home directory.
To begin, create the directory where your dotfiles will live. Many people use ~/.dotfiles or ~/dotfiles ; the name doesn't matter.
mkdir -p ~/.dotfiles
cd ~/.dotfiles
Next, move your existing configuration files to the repository . For example, if you have a .bashrc file in your home directory and want to manage it with Stow, you could do the following:
mv ~/.bashrc ~/.dotfiles/.bashrc
If you prefer the package-based approach, instead of leaving the file in the root of the repo, you would put it in a "bash" folder, keeping the full path:
mkdir -p ~/.dotfiles/bash
mv ~/.bashrc ~/.dotfiles/bash/.bashrc
The procedure with configurations within .config files is analogous: you replicate the directory structure within the repository . For example, for Neovim you might have:
mkdir -p ~/.dotfiles/nvim/.config/nvim
mv ~/.config/nvim/* ~/.dotfiles/nvim/.config/nvim/
Once the files are in your repository, it's a good idea to delete or rename the originals in $HOME to avoid conflicts. Later, Stow will recreate the symbolic links in the paths where applications expect to find their configs.
Install GNU Stow on different platforms
The installation of Stow varies slightly depending on the platform, but in general it's extremely straightforward. On macOS, the usual method is to use Homebrew , the most widely used package manager on this system:
brew install stow
In Linux distributions like Debian or Ubuntu, the usual approach is to use apt :
sudo apt install stow
On Arch Linux and derivatives, you can find it in the official repositories and install it with pacman without much mystery:
sudo pacman -S stow
Once installed, you'll have the "stow" command in your PATH. There are no daemons or background services, just a binary that runs when you need it . You can verify that everything is working correctly with "stow --version" and you're all set.
On systems where you already use tools like Oh My Zsh, Stow fits in very well: you can keep both your .zshrc file and your plugin and theme configurations in your central repository and apply everything with a couple of commands. Many users with multiple Macs or a mix of Linux and macOS systems report that this allows them to have the same shell and prompt everywhere.
Ignore unwanted files with .stow-local-ignore
One of Stow's finer points is its ignore system. By default, Stow already ignores certain typical version control files such as .git, .gitignore, .gitmodules, CVS directories, RCS directories, etc. However, there are situations where you need more specific control, for example, on macOS with the infamous .DS_Store.
Stow allows you to create a file called .stow-local-ignore in the directory from which you run the command. This file defines which patterns should be ignored locally. Once you create it, you stop using the default ignore list, so you have to add them yourself and include any extras.
A typical example of the contents of .stow-local-ignore would include comments and patterns for CVS conflicts, Emacs backups, version control files, and finally .DS_Store so that Stow doesn't complain or try to link those files generated by Finder:
# Comentarios y líneas en blanco permitidas
RCS
.+,v
CVS
\.#.+
\.cvsignore
\.svn
_darcs
\.hg
\.git
\.gitignore
\.gitmodules
.+~
\#.*\#
^/README.*
^/LICENSE.*
^/COPYING
.DS_Store
Thanks to this, you prevent Stow from trying to create links to completely irrelevant files and avoid annoying errors when stowing or unstowing packages . This is especially useful if you frequently browse your repository using graphical interfaces that generate auxiliary files.
Remember that Stow's ignore function is independent of the .gitignore file you use in your repository: the former controls what gets linked, the latter what gets versioned . Between the two, you can fine-tune the behavior of both Stow and Git.
Basic Stow usage: linking and unlinking configuration packages
With everything set up, Stow's daily operation is very straightforward. You should always run Stow from within your dotfiles repository directory , not from your $HOME or arbitrary paths, so that the relative paths it generates make sense.
Imagine you already have a module called “ghostty” in ~/dotfiles/ghostty/.config/ghostty with your configuration file. Once it's in the repository, you can store it with:
cd ~/dotfiles
stow ghostty
This command creates symbolic links on your system from ~/.config/ghostty to the files located in ~/dotfiles/ghostty/.config/ghostty. If you run "ls -l ~/.config/ghostty", you will see arrows (->) indicating the target of each symlink , confirming that everything is correctly linked.
If you prefer a unified approach and want to link everything at once, you can run “stow.” from the repo root . Stow will interpret each subdirectory as a package, or directly work on the structure if it's flat, and create symlinks for everything that fits.
To revert a specific package, simply call Stow with the -D option (for "delete" in the tool's terminology). For example:
cd ~/dotfiles
stow -D ghostty
This removes the symlinks I created for that module without affecting the original files that remain in the repository. It's a very clean way to "uninstall" configurations from a specific machine without losing them entirely.
It's crucial to avoid a very common mistake: don't run Stow from your home directory or any other folders outside the repository . Doing so risks creating shortcuts in unexpected places, cluttering your home directory with unwanted files. Always: `cd` to the repository, then `stow`.
Integrate Git and GitHub into your dotfile workflow with Stow
The beauty of this whole setup is combining Stow with Git so that your dotfiles are versioned, remotely backed up, and easily replicable on other machines . The process is very straightforward and no different from any other project you manage with Git.
From your dotfiles folder, initialize a new repository if you haven't already done so:
cd ~/dotfiles
git init
From there you can add your files, make commits, and work with branches as usual. A possible starting point could be:
git add .
git commit -m "Primer commit de mis dotfiles"
The next step is usually to create a repository on GitHub, GitLab, or another service and add it as a remote. Something like this:
git remote add origin [email protected]:tuusuario/dotfiles.git
git push -u origin main
Keep in mind that while some people publish their dotfiles in public repositories, it's generally best to use private repositories if you're handling sensitive data or paths that could reveal too much personal information. In any case, you can supplement this with external encryption for secrets if needed.
To fine-tune what gets versioned, it's also a good idea to have a .gitignore file in the repository where you add, for example, .DS_Store or other files you don't want to upload. Stow already ignores .git files, but Git doesn't know about .stow-local-ignore , so the two files serve different purposes and complement each other well.
The day-to-day process is very straightforward: clone your dotfiles repository to a new machine, install Stow, run “stow .” or “stow package1 package2…” and your environment is replicated . If you later change your Neovim configuration or your .zshrc file, you commit and push, and on the other machines, a simple `git pull` followed by `stow` is enough to update links if you've added new files or packages.
Passionate writer about the world of bytes and technology in general. I love sharing my knowledge through writing, and that's what I'll do on this blog, show you all the most interesting things about gadgets, software, hardware, tech trends, and more. My goal is to help you navigate the digital world in a simple and entertaining way.