Dotfiles and stow 📥

This post is 100% GenAI-free. Read here why.

There are many things I did not get right when I started doing research – back in my days 2015, when the ROS distro was Indigo, Python was at 2.7 but the default choice for robotics was mediocre C++ code or, worse, Matlab scripts. There are still many things that I do not get right today, but that’s a wider topic.

There is however one habit I picked up from a Linux savvy friend fairly early in my career (beginning of 2018, looking at the git history) that I think helped me streamline something that I find incredible tedious: setting up a pc after a fresh Linux install.

tl;dr, I have a Github repo dotfiles containing

  • dotfiles (duh) (.bashrc, .vimrc, .bash_aliases and so on)
  • an installation script setup.sh that I keep updated as my needs change and Ubuntu versioning advances.

On a fresh install,

git clone https://github.com/mattiaracca/dotfiles
source dotfiles/setup.sh

and… I have my machine back.

Disclaimer: Depending on your background, what I just described may be… the norm. If that’s the case, this post is not for you, I’m afraid. It’s for robotics researchers who, by nature of the field, come from disparate backgrounds: computer science, computer/mechanical/electrical engineering… and psychology, social sciences, and design if you look at the Human-Robot Interaction community.

Why versioning dotfiles

First, what are dotfiles? Brutally copy-pasted from this blog post

Dotfiles are hidden configuration files on a Unix-based system that are hidden by default due to their unique prefix (.). These files store configurations, settings, and preferences for applications and tools, such as version control systems and editors. They make it easy for developers to create customized environments personalized to their workflows.

Your .bashrc is a dotfile. The .config folder in your home is a standard place where applications expect dotfiles.

Why a dotfiles repo? Because having all my dotfiles in one place let’s me

  1. avoid remembering or googling stuff like does this dotfile go in .config/ or directly in ~/? or how to install zotero/zoom/dropbox/ros/texlive?.
  2. have a growing library of custom commands and settings that I can just git clone

It does make it hard for me to work on vanilla machines (e.g. computers of colleagues) because I don’t have my commands/aliases. But then again, that’s a pretty rare occasion.

Versioning dotfiles is not really arcane magic. What really does it for me is the use of stow and having a setup.sh.

Using stow to handle dotfiles

The idea comes from here and I’ll never be grateful enough. In short, you use the command stow to symlink your dotfiles (now neatly versioned in your dotfiles repo) where they are needed.

Let’s say I want the versioned ssh config file to go where it usually sits at ~/.ssh/config. In the repo root, I create a ssh folder, with inside the same folder structure of ~/.ssh/config (so a folder called .ssh with inside the config file).

From the repo root, I then stow ssh and now the config is correctly symlinked where needed. If you make changes inside the dotfiles folder, they are immediately applied (it’s a symlink). If you or something changes them in ~/.ssh/config, you’ll see it with git status, ready to be committed (or discarded).

Another example: .vimrc sits in ~/. In the repo, create a vim folder, put inside your .vimrc, stow vim, done.

This is what the tree command would give you from the root of the repo

dotfiles
├── ssh
│   └── .ssh
│       └── config
└── vim
    └── .vimrc

Machine specific dotfiles

What if I have many machines and some settings are wrong/nonsensical/unnecessary for some machine?

In that case, what works for me is to create machine specific scripts like mylaptop.sh and then have this at the end of my .bashrc

if [ -f ~/dotfiles/host_specific/$HOSTNAME.sh ]; then
    source ~/dotfiles/host_specific/$HOSTNAME.sh
fi

Like that, I have machine-specific commands, env variables, and the like.

The setup.sh

The setup script builds on all of the above and provides an entrypoint on a fresh install. These are excerpts of mine, just to give you an idea of what I mean

echo -e "\n===== Fresh installation =====\n"
sudo apt update
sudo apt install stow

read -p 'stow bash files? [y/n]: ' answer
if [ "$answer" = "y" -o -z "$answer" ];then
  rm ~/.bashrc
  rm ~/.bash_logout
  stow bash
fi

read -p 'Do you want vim? [y/n]: ' answer
if [ "$answer" = "y" -o -z "$answer" ];then
  sudo apt install vim
  stow vim
fi

It’s not just stowing. The script does things I always forget how to do, like, e.g. create a ssh key

read -p 'Create SSH key? [y/n]: ' answer
if [ "$answer" = "y" -o -z "$answer" ];then
  ssh-keygen -t ed25519 -C "definitively@not_my.email" -f ~/.ssh/id_ed25519
  ssh-add ~/.ssh/id_ed25519
fi

or install things that require more than a simple apt install, like e.g. zoom

read -p 'Do you want Zoom? [y/n]: ' answer
if [ "$answer" = "y" -o -z "$answer" ];then
  wget -O /tmp/zoom.deb https://zoom.us/client/latest/zoom_amd64.deb
  sudo apt -f install /tmp/zoom.deb
  (crontab -l ; echo "@reboot nohup setsid zoom")| crontab -
fi

The “secret” here is maintaining the script over time, adding new procedures to it (better when you are doing a procedure manually for the first time), removing stuff that became irrelevant or obsolete, etc.

I cannot estimate how much time this saved me (also because maintaining the repo does take some time every now and then). It sure improved my “Linux Hygiene” and made my actual work (doing research) much smoother.




    (Dis)liked what you read?

    Let me know! Drop me an email at mattia dot rh at gmail dot com with [BLOG] in the subject. Some other posts:

  • A writing experiment