Using stow to manage dotfiles on multiple systems

As a long time Linux user (almost 25 years now), there are a few tasks which have always been in the back of my mind but which I’ve always neglected. One of them is proper backups. I will come back later to that hopefully (I know). The other is a proper way to manage my dotfiles. I copy my dotfiles between my systems, try to move them to a new system and usually create a new version, trying to invent the wheel all over again every time. I’ve checked GNU stow before, but it did not really click in my mind, or maybe I wasn’t paying attention.

From the examples I saw, I thought the correct way to set up a stow directory was to put configs for different applications in different folders, and than use stow to link to it from your home folder. Like a folder for bash-config, another for bspwm, another for another program, etc. However this did not really made sense too much in my mind because I don’t use the same configuration even if it is the same app on different machines.

After reading for a while, I understand that this is not necessarily the only way to use stow. What I want to do is to have all config files from different “systems”, not different applications, in the same git repository and use stow to manage them.

I guess my use case is a bit different than usual. However I believe I have come to a sensible setup at the end. Here is how to understand the logic behind stow and how I set it up.

Understanding the structure stow expects

Stow expects a directory with subdirectories for each of the configuration groups. I will call this main stow directory “stow repository”. In the repo, each subfolder has config files or folders or whatever else you want to keep, and when you run stow subfolder command, it creates links to the files/folders in the subfolder, and places the links to the parent folder of the stow repo.

  1. You want to manage the dotfiles in your home directory, so the stow repository should be in your home directory.
  2. You need subfolders as configuration groups in the stow repo, which has files and folders that you want to manage in them.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
user@host:~$ pwd
/home/user
user@host:~$ ls -l
drwxr-xr-x 3 user user 4096 Aug 29 10:50 stow_repo
user@host:~$ cd stow_repo/
user@host:~/stow_repo$ ls -l
drwxr-xr-x 2 user user 4096 Aug 29 10:50 config_dir
user@host:~/stow_repo$ cd config_dir/
user@host:~/stow_repo/config_dir$ ls -l
-rw-r--r-- 1 user user 0 Aug 29 10:50 file_1
-rw-r--r-- 1 user user 0 Aug 29 10:50 file_2
user@host:~/stow_repo/config_dir$ cd ~/stow_repo/
user@host:~/stow_repo$ stow config_dir/
user@host:~/stow_repo$ cd
user@host:~$ ls -l
lrwxrwxrwx 1 user user   27 Aug 29 10:51 file_1 -> stow_repo/config_dir/file_1
lrwxrwxrwx 1 user user   27 Aug 29 10:51 file_2 -> stow_repo/config_dir/file_2
drwxr-xr-x 3 user user 4096 Aug 29 10:50 stow_repo
user@host:~$

Of course, these assumptions work for our use case but stow allows you to manage file linking in much more flexible ways. It is better explained in the links below [2]. However we don’t need to worry about these at the moment just to keep things simple.

My idea for structure

In my case, I want ~/.dotfiles to be my main stow repository with subfolders for each of my machines/systems:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
user@host:~$ cd .dotfiles/
user@host:~/.dotfiles$ ls -l
drwxr-xr-x 2 user user 4096 Aug 29 10:55 ceres-debian
drwxr-xr-x 2 user user 4096 Aug 29 10:55 common-configs
drwxr-xr-x 2 user user 4096 Aug 29 10:55 jupiter-arch
drwxr-xr-x 2 user user 4096 Aug 29 10:56 macbook-air
drwxr-xr-x 2 user user 4096 Aug 29 10:55 netbsd
drwxr-xr-x 2 user user 4096 Aug 29 10:56 pluto-ubuntu
drwxr-xr-x 2 user user 4096 Aug 29 10:56 scripts
drwxr-xr-x 2 user user 4096 Aug 29 10:56 wallpapers
user@host:~/.dotfiles$

Each system gets their own dotfiles within them:

1
2
3
4
5
6
7
8
9
user@host:~/.dotfiles$ cd netbsd/
user@host:~/.dotfiles/netbsd$ ls -la
drwxr-xr-x 2 user user 4096 Aug 29 10:58 .
drwxr-xr-x 9 user user 4096 Aug 29 10:56 ..
-rw-r--r-- 1 user user    0 Aug 29 10:58 .Xresources
-rw-r--r-- 1 user user    0 Aug 29 10:58 .mkshrc
-rw-r--r-- 1 user user    0 Aug 29 10:58 .vimrc
-rw-r--r-- 1 user user    0 Aug 29 10:58 .xsession
user@host:~/.dotfiles/netbsd$

And it does not have to be only files, it can be whole folders:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
user@host:~/.dotfiles$ cd jupiter-arch/
user@host:~/.dotfiles/jupiter-arch$ ls -la
-rw-r--r-- 1 user user    0 Aug 29 11:00 .bashrc
drwxr-xr-x 5 user user 4096 Aug 29 11:00 .config
user@host:~/.dotfiles/jupiter-arch$ cd .config/
user@host:~/.dotfiles/jupiter-arch/.config$ ls -l
drwxr-xr-x 2 user user 4096 Aug 29 11:00 elinks
drwxr-xr-x 2 user user 4096 Aug 29 11:00 nvim
drwxr-xr-x 2 user user 4096 Aug 29 11:00 tmux
user@host:~/.dotfiles/jupiter-arch/.config$

So, on a fresh NetBSD installation all I need to do is to install git and stow, clone the repository and stow what I need:

1
2
3
4
5
6
7
user@host:~$ su -
root@host:~$ pkgin in git stow
root@host:~$ exit

user@host:~$ git clone https://gitlab.com/fcn/dotfiles.git ~/.dotfiles
user@host:~$ cd .dotfiles/
user@host:~/.dotfiles$ stow netbsd

Whatever files that are in netbsd folder is now in my home directory.

Using a common-configs folder to manage common files

For configs that I want to share between different systems, I can place them in common-configs folder, and I can link them in the specific system I want to use this shared config.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
user@host:~/.dotfiles$ ls
ceres-debian  common-configs  jupiter-arch  macbook-air  netbsd  pluto-ubuntu  scripts  wallpapers
user@host:~/.dotfiles$ cd ceres-debian/
user@host:~/.dotfiles/ceres-debian$ ln -s ../common-configs/common-file .
user@host:~/.dotfiles/ceres-debian$ ls -l
lrwxrwxrwx 1 user user 29 Aug 29 12:14 common-file -> ../common-configs/common-file
user@host:~/.dotfiles/ceres-debian$ cd ..
user@host:~/.dotfiles$ stow ceres-debian/
user@host:~/.dotfiles$ cd ..
user@host:~$ ls -l
lrwxrwxrwx 1 user user   34 Aug 29 12:14 common-file -> .dotfiles/ceres-debian/common-file
user@host:~$

My actual setup from zero

I will start with a private repo in Gitlab to manage all files and to track changes in the future. I will go to my Gitlab projects page, New project → Create blank project and create a new project called dotfiles. This will give us a git repo with a single file so we can directly clone to our computer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
user@host:~$ git clone git@gitlab.com:fcn/dotfiles.git .dotfiles
Cloning into '.dotfiles'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (3/3), done.
user@host:~$ cd .dotfiles
user@host:~/.dotfiles$ ls
README.md
user@host:~/.dotfiles$ 

We have now our stow repository where we will place our config files which we will be able to track using git. We can just delete the README.md file or put some information and a link to this page in it for future reference.

I will then create folders for each system. I will start with the dotfiles on the Macbook and install stow using brew on MacOS.

1
2
3
4
5
6
7
user@host:~$ mkdir macbook-air
user@host:~$ cp ~/.zshrc macbook-air/
user@host:~$ mkdir macbook-air/.config
user@host:~$ cp -R ~/.config/{iterm2,starship.toml,zsh} macbook-air/.config/
user@host:~$ git add .
user@host:~$ git commit -m "added Macbook Air .zsh iterm2 and starship configs"
user@host:~$ git push

A problem we can face at this stage, is that sometimes there are other git repositories in our config folders. If we add a git-tracked folder in another repository, git will give us an error message and tell us that the main repo will not include these folders’ contents when cloned. In my case it is the zsh-autosuggestions repository in ~/.config/zsh/ folder, and I have decided not to include this ~/.config/zsh/ directory in my stow repo and add a note in my README.md to install starship separately (I believe this is how it was installed in the first place).

Other things in my ~/.dotfiles directory

I use keep my scripts and other resources I want to make available on my multiple systems such as a small selection of wallpapers or my customs scripts. I keep them in the same repository but not stow them at all. They are available directly in ~/.dotfiles and ~/.dotfiles/scripts is already added in my $PATH on systems that I want to use these scripts.

  1. https://www.jakewiesler.com/blog/managing-dotfiles
  2. https://wesley.schwengle.net/article/managing-my-dotfiles-with-gnu-stow-a772/
  3. https://www.gnu.org/software/stow/manual/