Adopting FreeDesktop.org XDG standard for development tools
Managing your personal configuration for development tools and applications is much more effective when adopting the XDG basedir standard, which defines separate locations to store user configurations, data files and caches.
Without the XDG standard, these files and directories are often mixed together and stored in the $HOME
of the users account, making it more challenging to backup or version control.
Development tools such as NeoVim, Emacs, Clojure CLI and Clojure LSP support the XDG specification, although some tools like Leiningen required a little help. There are simple approaches to work-around the limitations of tools that don't conform.
FreeDesktop XDG Standards
FreeDesktop.org produces standards under the Cross-Desktop Group, referred to as XDG.
Two of the most relevant specifications for users are:
- Desktop base directories (basedir): defining the location of application configuration and data files
- Desktop entries (.desktop): to define executable, application name, icon and description, used by application launchers and desktop menus
This article will focus on the basedir configuration and document the XDG locations for a range of common developer tools
XDG Environment variables
The XDG Base Directory Specification separates configuration, data, runtime and cache files in separate locations, e.g. .config
, .cache
, etc..
Each location organises application specific files within a directory of the same name as that application.
XDG_CONFIG_HOME
user-specific configuration files, default$HOME/.config
XDG_DATA_HOME
user-specific data files. default$HOME/.local/share
XDG_STATE_HOME
user-specific state data$HOME/.local/state
XDG_CACHE_HOME
user-specific non-essential (cached) data, default$HOME/.cache
XDG_RUNTIME_DIR
runtime files bound to the login status of the user
XDG_CONFIG_DIRS
and XDG_DATA_DIRS
can be used to define an ordered set of directories to search for their respective files, rather than just a single directory.
Environment variables must be set to an absolute path to be consider valid. Values can include other environment variables, e.g. HOME, as long as the path resolves as absolute.
A detailed description is covered in the freedesktop.org basedir specification.
MacOSX XDG Paths
According to Apple documentation, the XDG Base directory specifications should use these locations
XDG_CONFIG_HOME
-︎~/Library/Preferences/
using reverse domain name notation:com.apple.AppStore.appname
XDG_DATA_HOME
-︎~/Library/
XDG_CACHE_HOME
-︎~/Library/Caches/
See the Mac OS X Reference Library: Where to Put Application Files and Mac OS X Reference Library: Important Java Directories on Mac OS X
Configuring Linux / Unix operating system
The XDG basedir specification is used by many Unix distributions, although the XDG_CONFIG_HOME
is rarely set as it may break older applications. Instead the OS uses the default locations in the specifications and expects applications to do the same.
Some tools already use the default location of XDG_CONFIG_HOME
. Most maintained tools will use the XDG locations if the environment variables are set.
Defining XDG_CONFIG_HOME
before installing development tools and applications helps ensure that the right locations are used.
Existing configurations are easily migrated to the XDG_CONFIG_HOME
directory, either all at once or application by application. If the configuration is not found in XDG_CONFIG_HOME
the application should look for the configuration in the HOME
directory.
Where to Set environment variables
For desktop launchers, edit the .profile
file and export a value for XDG_CONFIG_HOME
, which should then be used by all applications launched in this way.
For example, the XDG_CONFIG_HOME
is defined for all applications and SPACEMACSDIR
is defined specifically for Spacemacs configuration for Emacs.
# Ensure XDG_CONFIG_HOME is set when launching apps from destktop
export XDG_CONFIG_HOME=$HOME/.config
# Application specific
export SPACEMACSDIR=$XDG_CONFIG_HOME/spacemacs
# Recommended locations
export XDG_DATA_HOME=$HOME/.local/share
export XDG_CACHE_HOME=$HOME/.cache
export XDG_STATE_HOME=$HOME/.local/state
To apply these environment variables, logout of the desktop environment, then login again. Now the environment variables are set.
To test before logout/login, in a terminal run the command source ~/.profile
and the environment variables will be available in that terminal session.
Command Line
The shell and all command line tools will use environment variables in ~/.profile
. Or they can be set in the respective resource files for the terminal shell to add environment variables
- bash - use
~/.bashrc
- zsh - use
~/.zprofile
or.zshenv
For example, in ~/.zprofile
:
# Set XDG_CONFIG_HOME for clean management of configuration files
export XDG_CONFIG_HOME="$XDG_CONFIG_HOME=$HOME/.config"
Zsh - prezto
Prezto is an rich configuration for Zsh that provides aliases, functions, auto completion and prompt themes (including the popular Powerline10k theme). Prezto includes a wide range of optional modules such as git status, autosuggestions (fish shell completion), GPG, etc.
Install prezto in the XDG_CONFIG_HOME/zsh
directory.
git clone --recursive https://github.com/sorin-ionescu/prezto.git "${XDG_CONFIG_HOME}/prezto"
Prezto requires ~/.zshenv
to bootstrap the location of prezto configuration. The ~/.zshenv
file can be a symbolic link to XDG_CONFIG_HOME/zsh/.zshenv
Set the XDG_CONFIG_HOME
location to HOME/.config
for all applications if not set in ~/.profile
Set the ZDOTDIR
location so Zsh can find the Prezto configuration.
# Set XDG_CONFIG_HOME for clean management of configuration files
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}"
export ZDOTDIR="${ZDOTDIR:=$XDG_CONFIG_HOME/zsh}"
Other application specific environment variables should be added to .zshevn
file as well, e.g. SPACEMACSDIR
for Spacemacs configuration.
TIP: Once installed, any prezto configuration files that will be added can be copied from the
.zprezto/runcoms
directory toXDG_CONFIG_HOME/zsh
, rather than linking as the documentation suggests. This approach minimises the need to merge changes when updating prezto.
Neovim
Neovim supports the XDG basedir specification and will use ~/.config/nvim
directory by default.
All the packages installed, such as the excellent Conjure for Clojure development (and many other fun languages), will therefore be part of the ~/.config/neovim
configuration.
Vim does not support the basedir. However, when Neovim is installed as a .deb package it will be used for the vim command (set via /etc/alternatives/vim
)
Practicalli Neovim is the start of a new book on setting up Neovim with Clojure, LSP, Treesitter and many other packages, using fennel (a lisp dialect) for its configuration. More details coming soon.
Emacs and Spacemacs Configuration
Emacs will use XDG_CONFIG_HOME/emacs
location if it contains an init.el
file.
SPACEMACSDIR
environment variable is used by Spacemacs to set the dotspacemacs-directory
, defining where to look for the Spacemacs specific init.el
file.
Set SPACEMACSDIR
to XDG_CONFIG_HOME/spacemacs
# Ensure XDG_CONFIG_HOME is set when launching apps from destktop
export XDG_CONFIG_HOME="$HOME/.config"
# Ensure Emacs can find the location of Spacemacs configuration
# when using a desktop launcher
export SPACEMACSDIR="$XDG_CONFIG_HOME/spacemacs"
Move the .spacemacs
file to XDG_CONFIG_HOME/spacemacs/init.el
. Or if using ~/.spacemacs.d/
then move that directory to be the XDG_CONFIG_HOME/spacemacs/
directory.
authinfo secure credentials
Connection credentials that include sensitive data (passwords, developer tokens) can be stored in authinfo.pgp
, a PGP encrypted file. This providing an extra level of security for sensitive data.
For example, Magit Forge uses authinfo.gpg to define a connection to GitHub or GitLab that includes a developer token.
;; Use XDG_CONFIG_HOME location or HOME
(setq auth-sources (list
(concat (getenv "XDG_CONFIG_HOME") "/authinfo.gpg")
"~/.authinfo.gpg"))
Doom Emacs
If Emacs configuration is detected in $HOME/.config/emacs
then Doom will install its configuration in $HOME/.config/doom
, so long as DOOMDIR
has not already been configured to a different location by the user.
Git
Git will write to and read from XDG_CONFIG_HOME/git/config
as its configuration, if that file exists and ~/.gitconfg
does not exist. Otherwise ~/.gitconfg
is used.
Before running any git
commands, create a config file in the XDG location
mkdir $XDG_CONFIG_HOME/git && touch $XDG_CONFIG_HOME/git/config
git config
commands will now update the XDG configuration file, e.g. setting the user identity and configure diff to use the diff3 (shared parent) for merge conflicts
git config --global user.name "John Practicalli"
git config --global user.email "******+account-name@users.noreply.github.com"
git config --global merge.conflictstyle diff3
Add an ignore-global file to the XDG_CONFIG_HOME/git
directory. Example excludes files can be found at github/gitignore repository or at practicalli/dotfiles
Add an excludes file to the Git config file containing the patterns used across all the users projects.
git config --global core.excludesFile ~/.config/git/ignore-global
The git ignore file should be defined with the full path so tools like projectile can find that ignore file.
XDG_CONFIG_HOME/git/template
is a common location for scripts and hooks that should be added to a newly created Git repository, in the .git
directory. The template location is set by init.templatedir
mkdir $XDG_CONFIG_HOME/git/template
git config --global init.templatedir template
Clojure CLI
The XDG_CONFIG_HOME/clojure
directory is the location for the Clojure CLI user level configuration files (e.g. deps.edn
, tools/tools.edn
).
If XDG_CONFIG_HOME
is not set or that location is not found, then HOME/.clojure
is used instead.
If CLJ_CONFIG
is set to a value, then Clojure CLI commands will use that instead.
Also see Maven and dependencies to manage the
$HOME/.m2/repository
directory
Maven and dependencies
Clojure CLI and Leiningen use the Maven configuration directory to store Jar files from project (and tooling) dependencies, by default this is located in $HOME/.m2/repository
.
Jar files from dependencies are considered non-essential (to the Clojure CLI tool) so should be written to the $XDG_CACHE_HOME location, typically $HOME/.cache
Add the :mvn/local-repo
top-level key in the user level deps.edn file to set a location for the Maven repository. The value should be the full directory path.
:mvn/local-repo "/home/practicalli/.cache/maven/repository"
The :mvn/local-repo
can also be used in a project deps.edn file or on the command line, i.e. clojure -Sdeps '{:mvn/local-repo ".cache/temp-deps"}'
if the Maven dependencies should be kept separate from all other projects (this scenario is not common).
The Maven $HOME/.m2
directory also contains several configuration files, maven.config
, jvm.config
and extensions.xml
, so unfortunately conflates configuration files with data files. Although Clojure CLI does not use these configuration files, it is useful to separate the jar files into a cache.
Clojure Gitlibs
Clojure CLI can used dependencies from Git repositories. To do so, the repository is downloaded into a $HOME/.gitlibs
directory, unless the GITLIBS
environment variable is set. As the gitlibs
directory contains data for the application, then ideally this would be placed in XDG_CACHE_HOME
, under a clojure-gitlibs
directory
Set the GITLIBS
environment variable to determine the location of the local cache directory used to clone dependencies that are Git repositories.
export $GITLIBS=$XDG_CACHE_HOME/clojure-libs
Optionally move the existing $HOME/.gitlibs
to the Cache home.
mv $HOME/.gitlibs $XDG_CACHE_HOME/clojure-gitlibs
Clojure LSP
XDG_CONFIG_HOME/clojure-lsp
directory is used as the location for Clojure LSP configuration if XDG_CONFIG_HOME
is set. Otherwise, $HOME/.clojure-lsp
is used as the configuration.
If Clojure LSP was used for for a while, configuration may be in the deprecated
~/.lsp
directory.
Intellij Idea and Cursive
Intellij Idea from version 2020.1 uses the XDG basdir specification locations without the need to set XDG_CONFIG_HOME.
Configuration is organised under the ~/.config/JetBrains/ directory with product and version sub-directories, for example:
~/.config/JetBrains/IntelliJIdea2022.1
Intellij IDEA 2020.1 documentation - configuration directories
Leiningen
Leiningen does not support configuration in XDG_CONFIG_HOME
, although there is an outstanding issue to add this as an enhancement from 2016.
Once Leiningen is installed, a temporary work-around would be to move the ~/.lein
directory to XDG_CONFIG_HOME/leiningen
and create a symbolic link called ~/.lein
mv ~/.lein $XDG_CONFIG_HOME/leiningen
ln -s $XDG_CONFIG_HOME/leiningen ~/.lein
The
lein
script usesLEIN_HOME
variable, although this is hard-coded in the script to$HOME/.lein
and therefore is not usable to set as an operating system environment variable to change the configuration directory location.
VS Code and Calva
Unfortunately XDG basedir specification is not currently supported by VS Code and therefore Calva (although I assume the Clojure LSP tooling in Calva will use XDG_CONFIG_HOME)
There is an outstanding issue on the VS Code, also from 2016, to revisit the configuration structure and support XDG_CONFIG_HOME.
Using the symbolic link workaround, as with Leiningen, should work for VS Code too.
mv $HOME/.vscode $XDG_CONFIG_HOME/vscode
ln -s $XDG_CONFIG_HOME/vscode $HOME/.vscode
It is advisable to close VS Code before moving the configuration, to ensure the configuration has been saved.
Summary
The XDG basedir specification makes it simple to back up and version a users configuration files as they are all under the one directory, i.e. $HOME/.config
As data and cache files are in different locations, then few if any ignore files are required to version the configuration files.
Migrating is relatively quick and painless and applications can be migrated over time if required.
Thank you