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.
Vim
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.
Zsh
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:
LESSHISTFILE=${XDG_CACHE_HOME:-$HOME/.cache}/lesshist
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.