XDG Base Directory Specification
Quick Reference
# XDG directories
echo $XDG_CONFIG_HOME # ~/.config
echo $XDG_DATA_HOME # ~/.local/share
echo $XDG_CACHE_HOME # ~/.cache
echo $XDG_STATE_HOME # ~/.local/state
echo $XDG_RUNTIME_DIR # /run/user/UID
# Check if set, use default if not
${XDG_CONFIG_HOME:-$HOME/.config}
${XDG_DATA_HOME:-$HOME/.local/share}
# List XDG user directories
cat ~/.config/user-dirs.dirs
xdg-user-dir DESKTOP
xdg-user-dir DOWNLOAD
Understanding XDG Standards
Why XDG?
The XDG Base Directory Specification solves the "dotfile mess" in home directories:
Without XDG:
~/.bashrc ~/.bash_history ~/.vimrc ~/.vim/ ~/.gitconfig ~/.npmrc ~/.cargo/ ~/.local/bin/ ... hundreds more
With XDG:
~/.config/ # Configuration files ~/.local/share/ # Application data ~/.cache/ # Cached data ~/.local/state/ # State data
XDG Environment Variables
Required Variables
| Variable | Default | Purpose |
|---|---|---|
XDG_CONFIG_HOME |
~/.config |
User-specific configuration |
XDG_DATA_HOME |
~/.local/share |
User-specific data files |
XDG_CACHE_HOME |
~/.cache |
User-specific cache data |
XDG_STATE_HOME |
~/.local/state |
User-specific state data |
XDG_RUNTIME_DIR |
/run/user/UID |
Runtime files (sockets, etc.) |
System-Wide Variables
| Variable | Default | Purpose |
|---|---|---|
XDG_DATA_DIRS |
/usr/local/share:/usr/share |
System data search paths |
XDG_CONFIG_DIRS |
/etc/xdg |
System config search paths |
Set XDG Variables
# ~/.bashrc or ~/.zshrc
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_DATA_HOME="$HOME/.local/share"
export XDG_CACHE_HOME="$HOME/.cache"
export XDG_STATE_HOME="$HOME/.local/state"
# Create directories if needed
mkdir -p "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" "$XDG_STATE_HOME"
Using Variables in Scripts
# Always use fallback for portability
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/myapp"
data_dir="${XDG_DATA_HOME:-$HOME/.local/share}/myapp"
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/myapp"
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/myapp"
# Create app directories
mkdir -p "$config_dir" "$data_dir" "$cache_dir" "$state_dir"
Directory Structure
XDG_CONFIG_HOME (~/.config)
Configuration files that users may edit:
~/.config/
├── git/
│ └── config # Git configuration
├── nvim/
│ └── init.lua # Neovim configuration
├── systemd/
│ └── user/ # User systemd units
├── containers/
│ └── containers.conf # Podman configuration
├── alacritty/
│ └── alacritty.toml # Terminal configuration
└── hypr/
└── hyprland.conf # Hyprland configuration
XDG_DATA_HOME (~/.local/share)
Application data that should be preserved:
~/.local/share/
├── applications/ # Desktop entries
├── fonts/ # User fonts
├── icons/ # User icons
├── nvim/
│ └── site/ # Neovim plugins
├── gnupg/ # GPG keyrings
├── password-store/ # pass passwords
└── containers/
└── storage/ # Podman images
XDG_CACHE_HOME (~/.cache)
Non-essential cached data (can be deleted):
~/.cache/
├── pip/ # Python packages
├── npm/ # Node packages
├── go-build/ # Go build cache
├── thumbnails/ # Image thumbnails
└── fontconfig/ # Font cache
XDG User Directories
Standard User Directories
# View current settings
cat ~/.config/user-dirs.dirs
# Standard directories
XDG_DESKTOP_DIR="$HOME/Desktop"
XDG_DOWNLOAD_DIR="$HOME/Downloads"
XDG_TEMPLATES_DIR="$HOME/Templates"
XDG_PUBLICSHARE_DIR="$HOME/Public"
XDG_DOCUMENTS_DIR="$HOME/Documents"
XDG_MUSIC_DIR="$HOME/Music"
XDG_PICTURES_DIR="$HOME/Pictures"
XDG_VIDEOS_DIR="$HOME/Videos"
Application Compliance
Applications with XDG Support
Many applications support XDG natively:
# Git (since 2.x)
~/.config/git/config
~/.config/git/ignore
# Neovim
~/.config/nvim/init.lua
# Tmux
~/.config/tmux/tmux.conf
# Fish shell
~/.config/fish/config.fish
# Alacritty
~/.config/alacritty/alacritty.toml
# mpv
~/.config/mpv/mpv.conf
# systemd user units
~/.config/systemd/user/
Force XDG Compliance
Some applications need environment variables:
# ~/.bashrc or ~/.zshrc
# Bash
export HISTFILE="${XDG_STATE_HOME:-$HOME/.local/state}/bash/history"
mkdir -p "$(dirname "$HISTFILE")"
# Less
export LESSHISTFILE="${XDG_STATE_HOME:-$HOME/.local/state}/lesshst"
# Wget
export WGETRC="${XDG_CONFIG_HOME:-$HOME/.config}/wget/wgetrc"
alias wget='wget --hsts-file="${XDG_DATA_HOME:-$HOME/.local/share}/wget/hsts"'
# GNU Readline
export INPUTRC="${XDG_CONFIG_HOME:-$HOME/.config}/readline/inputrc"
# Python
export PYTHONSTARTUP="${XDG_CONFIG_HOME:-$HOME/.config}/python/startup.py"
export PYTHON_HISTORY="${XDG_STATE_HOME:-$HOME/.local/state}/python/history"
# Node.js
export NODE_REPL_HISTORY="${XDG_STATE_HOME:-$HOME/.local/state}/node/history"
export NPM_CONFIG_USERCONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/npm/npmrc"
export NPM_CONFIG_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/npm"
# Rust/Cargo
export CARGO_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/cargo"
export RUSTUP_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/rustup"
# Go
export GOPATH="${XDG_DATA_HOME:-$HOME/.local/share}/go"
export GOMODCACHE="${XDG_CACHE_HOME:-$HOME/.cache}/go/mod"
# Docker
export DOCKER_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/docker"
# GnuPG
export GNUPGHOME="${XDG_DATA_HOME:-$HOME/.local/share}/gnupg"
# Pass (password-store)
export PASSWORD_STORE_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/pass"
XDG MIME Applications
Default Applications
# View default application for type
xdg-mime query default text/html
xdg-mime query default application/pdf
# Set default application
xdg-mime default firefox.desktop text/html
xdg-mime default org.pwmt.zathura.desktop application/pdf
# Query file type
xdg-mime query filetype document.pdf
MIME Database
# User MIME associations
~/.config/mimeapps.list
# Example mimeapps.list
[Default Applications]
text/html=firefox.desktop
application/pdf=org.pwmt.zathura.desktop
image/png=imv.desktop
video/mp4=mpv.desktop
[Added Associations]
text/plain=nvim.desktop;code.desktop
# Update MIME database
update-mime-database ~/.local/share/mime
Desktop Entries
Desktop Entry Location
# User desktop entries
~/.local/share/applications/
# System desktop entries
/usr/share/applications/
# Desktop entry example
~/.local/share/applications/myapp.desktop
Create Desktop Entry
# ~/.local/share/applications/myapp.desktop
cat > ~/.local/share/applications/myapp.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=My Application
Comment=A cool application
Exec=/usr/bin/myapp %f
Icon=myapp
Terminal=false
Categories=Utility;
MimeType=text/plain;
EOF
# Update desktop database
update-desktop-database ~/.local/share/applications
Autostart
XDG Autostart
# User autostart directory
~/.config/autostart/
# System autostart
/etc/xdg/autostart/
# Create autostart entry
cat > ~/.config/autostart/myapp.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=My Startup App
Exec=/usr/bin/myapp --daemon
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
EOF
Managing XDG Files
Backup Strategy
# Back up configuration
tar czf config-backup.tar.gz -C ~ .config
# Back up important data
tar czf data-backup.tar.gz -C ~/.local share
# Exclude cache from backup
# Cache can be regenerated
Troubleshooting
Check XDG Variables
# Print all XDG variables
env | grep XDG
# Verify directories exist
ls -la "${XDG_CONFIG_HOME:-$HOME/.config}"
ls -la "${XDG_DATA_HOME:-$HOME/.local/share}"
ls -la "${XDG_CACHE_HOME:-$HOME/.cache}"
ls -la "${XDG_STATE_HOME:-$HOME/.local/state}"
ls -la "${XDG_RUNTIME_DIR}"
Quick Reference
# Environment variables
XDG_CONFIG_HOME ~/.config # User configuration
XDG_DATA_HOME ~/.local/share # User data
XDG_CACHE_HOME ~/.cache # User cache
XDG_STATE_HOME ~/.local/state # User state
XDG_RUNTIME_DIR /run/user/UID # Runtime files
# User directories
xdg-user-dir DESKTOP # ~/Desktop
xdg-user-dir DOWNLOAD # ~/Downloads
xdg-user-dir DOCUMENTS # ~/Documents
# MIME handling
xdg-mime query default TYPE # Get default app
xdg-mime default APP.desktop TYPE # Set default app
xdg-open FILE # Open with default
# Locations
~/.config/mimeapps.list # MIME associations
~/.local/share/applications/ # Desktop entries
~/.config/autostart/ # Autostart entries
~/.config/user-dirs.dirs # User directory config