How I manage my dotfiles

Posted July 15, 2021 by Adrian Wyssmann ‐ 4 min read

It is common practice to track dotfiles with a version control system like Git and to synchronize them across various hosts. There are various approaches, so I would like to tell you how I do it

What are dotfiles?

Dotfiles in Linux are hidden file - thus the name, as all files with a preseeding dot are hidden - which contain user-specific configuration of applications, for example the .bashrc.

Motivation to manage them

Well the primary motivation is to easily re-use the config files on a new machine. A backup might do it also, but usually you may want to use these files also on other machines of yours - if you have some. In addition, as other developers do as well, I want to share what I have as it may also benefit others.

My current solution

Sharing is usually done having your dotfiles in a git repo, which is checked-out somewhere in your home e.g. ~/.dotfiles/. But how do you bring them to the right place? Well the simplest solution is having symlinks i.e. ~/.bashrc is a symlink to the actual git-repo which may be example in ~/.dotfiles/shell/.bashrc. This means whenever you change the content in ~/.bashrc the changes are reflected it the git repo ~/.dotfiles/, which then can be easily committed and pushed to the remote repo. As you don’t want to manage these symlinks manually, I found stow, a symlink manager:

a symlink farm manager which takes distinct packages of software and/or data located in separate directories on the filesystem, and makes them appear to be installed in the same place

What I actually did is setup the repo in a way, that you can have different “profiles” example one for your personal use and one for your work. I perform the setup with a setup.sh

#!/bin/bash
# read the option and store in the variable, $option
TARGETDIR=~/
DEFAULTPROFILE=personal
RESTOW=
ADOPT=

# Function: Print a help message.
usage() {
  echo "Usage: $0 [ -p PRFOILE ] -R PACKAGNAME|all" 1>&2 
}

# Function: Exit with error.
exit_abnormal() {
  usage
  exit 1
}

while getopts "aRDp:" option; do
   case ${option} in
      a )
         echo "do an 'adopt'"
         ADOPT="--adopt --override='.*'"
         ;;
      R )
         echo "do a 'restow' i.e. stow -D followed by stow -S"
         RESTOW="-R"
         ;;
      D )
         echo "delete i.e. stow -D followed by stow -S"
         DELETE="-R"
         ;;
      p )
         PROFILE=${OPTARG}
         echo "profile '$PROFILE' selected\n"c
         ;;
      \? )
         exit_abnormal
      ;;
      *)
         exit_abnormal
      ;;
    esac
done

if [ ! $PROFILE ]; then
   if [ $DEFAULTPROFILE ]; then
      echo "using default profile '$DEFAULTPROFILE'"
      PROFILE=$DEFAULTPROFILE
   else
      echo "-p PROFILE was not specified"
      exit_abnormal
   fi
fi

SOURCE=$(pwd)/$PROFILE
if [ ! -d "$SOURCE" ]; then
   echo "Invalid profile '$PROFILE' (path '$SOURCE' missing)"
   exit_abnormal
fi

pushd $SOURCE

shift $(($OPTIND - 1))
if [ ! $1 ]
then
   echo "no packages specified. syou can use 'all' if you want to install all from the profile or use one of these:"
   echo $(ls)
   exit_abnormal
else
   PACKAGE=$1
   if [ $PACKAGE == "all" ]
   then
      echo "Install all available packages"
      for filename in $(find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n'); do
         echo stow $RESTOW $DELETE $ADOPT $filename -t $TARGETDIR
         stow $RESTOW $DELETE $ADOPT $filename -t $TARGETDIR
      done
   elif [ -d "./$PACKAGE" ]
   then
      echo stow $RESTOW $DELETE $ADOPT $PACKAGE -t $TARGETDIR
      stow $RESTOW $DELETE $ADOPT $PACKAGE -t $TARGETDIR
   else
      echo "package '$PACKAGE' missing"
   fi
fi

popd

You can find details in my repo papanito/dot-files. As this solutions does obviously not support encryption, I have a second private repo, where I store more sensitive data like .ssh/config. However, the script I use there is the same. As I manage my whole dev-environment with [ansible], I don’t use my setup script, but my own ansible role to achieve the same.

Am I happy with that solution? Well yes and no. It fulfills its purpose but has some drawbacks

  • as mentioned, it’s not really suitable for storing sensitive data
  • stow requires that the files do not exists, so for an initial setup you have to manually delete/move the existing files, so that the symlink can be create

What are alternatives?

One interesting alternative is the solution proposed on Archlinux Wiki, where you set --work-tree=$HOME instead of creating symlinks:

$ git init --bare ~/.dotfiles
$ alias config='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
$ config config status.showUntrackedFiles no

This seems interesting, but has the same issue with the sensitive data. With my solution I can keep at least my sensitive config files stored as well - even so it still may not be the best solution.

There are other solutions and if you are interested Archlinux Wiki has a list of some interesting tools to manage your dotfiles. Some of these solutions also tackles problems like encryption of sensitive data. I did not (yet) try these tools, but maybe, if I find some time I will have a look at it.