The Dots

I Try Arch (btw)  |      17.02.2023   4min read 

As opposed to the system-level configuration, which I describe in configuration management, the dotfiles are all my user-level configuration. To reproduce my setup on a newly configured machine, I have to transfer all my configuration (i.e. dotfiles) to it, so I need a clean way to manage them.

Most software is kind enough to respect the XDG Base Directory specification and thus puts it’s configuration in $XDG_CONFIG_HOME, which defaults to $HOME/.config. I manage my .config directory as a git repository. While not being the ideal solution for dotfile management, it was sufficient enough for me to start.

However, not every software respects the specification but “pollutes” the home directory instead. Awesome as the Arch wiki is, it has a list of applications and how well they support XDG Base Directory, as well as workarounds if it’s only partially supported.

In my case, these candidates are Vim and zsh. There are some more files and directories like .gitconfig, ~/.docker and ~/.ssh, however I’m fine with these as they contain environment specific configuration like my ssh host aliases, and I don’t need them under version control and certainly don’t want them in my public dotfiles repo.


For a moment I thought about using Neovim for it’s built-in XDG Base Directory support, but the Arch wiki lists this awesome blog which offers a complete solution to make vim respect it as well. It basically boils down to setting all of vim’s paths (runtimepath, packpath, viewdir, backupdir, undodir, etc) to their respective locations in your XDG dirs. In addition to that, I had to adjust the directory in vim-plug’s initialization, like so:

call plug#begin($XDG_DATA_HOME.'/vim/plugged')

To make vim pick up the file in the new location I set $VIMINIT to something like source ~/.config/vim/vimrc via pam_env, so it’s always set.


Basically the same is true for zsh. I moved the dotfiles to $XDG_CONFIG_HOME/zsh and set $ZDOTDIR accordingly, also via pam_env. Additionally, I moved the history file to $XDG_DATA_HOME by setting $HISTFILE. At this point, all dotfiles were nicely stowed in their directory, however the .zcompdump file was bugging me. On startup, zsh compiles it’s completions for better performance and dumps them as a script into this file. That seems not like a file to put under version control, does it? More like it belongs into a place like $XDG_CACHE_HOME, I think. And it happens that I’m not the only one who thinks this, as there’s already a PR open for oh-my-zsh (in my particular case the creation of the file was due to the oh-my-zsh framework, others might do it differently).

During my research, I also stumbled across this little snippet, which keeps wget from putting its hsts file in my home directory.

alias wget="wget --hsts-file=${XDG_CACHE_HOME:-$HOME/.cache}/wget-hsts"

And also basically the same for less:


So with all that, I (nearly) have a nice, uncluttered home directory, and even more important, I can transfer my config directory to a new machine and instantly get my setup. Awesome 🎉

(Won’t?) Fix

Unfortunately there are also apps which have their config file and user data location hardcoded and no workaround is available. In my case, at the time of writing this, these are .mozilla and .ansible. While the former has a 17 year old bugreport and even a recent work-in-progress, the latter only has stubborn developers. Altough I can life with that, I find it a real shame that a project as awesome as ansible blocks adopting a commonly accepted standard with such flimsy arguments.

Update: The environment is changing

Just recently I discovered that linux-pam has deprecated reading the user environment (that is the ~/.pam_environment file), so I (or basically everyone) need another way to set aforementioned environment variables. Currently people are discussing multiple strategies, but “the best” solution hasn’t emerged yet. I’m a fan of using systemd’s environment.d, but that only is applied to systemd user services. There is, however, systemd#7641 which discusses making them visible for login sessions.

For the time being, I stuck to using the .zshenv file to set $ZDOTDIR so .zshrc get’s picked up, and then set everything else from there.

Update to the update

While refactoring my session startup, I needed a different solution to this, since, using a display manager, the window manager was no longer started through the shell. Due to that, I moved everything from .zshrc into environment.d and use the systemd-environment-d-generator(8) to import them into the shell and the window manager session.