| name | dotfiles |
| description | Dotfile management with stow, chezmoi, yadm, or git bare repo for configuration sync across machines. Use when user asks to "manage dotfiles", "sync configs", "setup dotfiles", "backup shell config", "organize config files", "bootstrap new machine", "stow packages", "chezmoi template", "git bare repo dotfiles", "XDG directories", "shell startup files", "gitconfig aliases", "tmux config", or manage any ~/. configuration files. |
Dotfiles
Manage and sync configuration files across machines.
Management Strategy Comparison
| Tool | Complexity | Symlinks | Templates | Encryption | Multi-machine |
|---|
| Git bare repo | Low | No (actual files) | No | No | Manual |
| GNU Stow | Low | Yes | No | No | Manual |
| chezmoi | Medium | No (copies) | Yes | Yes | Built-in |
| yadm | Medium | No (actual files) | Yes (Jinja) | Yes (GPG) | Built-in |
Git Bare Repo (Minimal)
No dependencies, no symlinks. Home directory is the work tree.
git init --bare $HOME/.dotfiles
alias dot='git --git-dir=$HOME/.dotfiles --work-tree=$HOME'
dot config --local status.showUntrackedFiles no
echo "alias dot='git --git-dir=\$HOME/.dotfiles --work-tree=\$HOME'" >> ~/.zshrc
dot add ~/.zshrc ~/.gitconfig
dot commit -m "Add shell and git config"
dot remote add origin git@github.com:user/dotfiles.git
dot push -u origin main
git clone --bare git@github.com:user/dotfiles.git $HOME/.dotfiles
alias dot='git --git-dir=$HOME/.dotfiles --work-tree=$HOME'
dot checkout
dot checkout 2>&1 | grep -E "^\s+" | xargs -I{} mv {} {}.bak
dot checkout
dot config --local status.showUntrackedFiles no
GNU Stow (Symlink Manager)
brew install stow
apt install stow
mkdir -p ~/dotfiles && cd ~/dotfiles && git init
Each top-level folder is a "package" mirroring home directory structure:
~/dotfiles/
├── zsh/
│ └── .zshrc
├── git/
│ └── .gitconfig
├── nvim/
│ └── .config/nvim/
│ └── init.lua
├── tmux/
│ └── .tmux.conf
└── scripts/
└── .local/bin/
└── backup.sh
Usage
cd ~/dotfiles
stow zsh
stow */
stow -D zsh
stow -R zsh
stow -t /etc/nginx nginx
stow -n -v zsh
Conflict Resolution and Ignore
mv ~/.zshrc ~/.zshrc.bak && stow zsh
stow --adopt zsh
Create .stow-local-ignore in any package to exclude files: README.md, LICENSE, \.git.
chezmoi (Full-Featured)
brew install chezmoi
chezmoi init
chezmoi init --apply https://github.com/user/dotfiles.git
chezmoi add ~/.zshrc
chezmoi add --autotemplate ~/.gitconfig
chezmoi add --encrypt ~/.ssh/id_ed25519
chezmoi edit ~/.zshrc
chezmoi diff
chezmoi apply
chezmoi update
Templates
# ~/.local/share/chezmoi/dot_gitconfig.tmpl
[user]
name = {{ .name }}
email = {{ .email }}
{{ if eq .chezmoi.os "darwin" }}
[credential]
helper = osxkeychain
{{ end }}
{{ if eq .chezmoi.hostname "work-laptop" }}
[http]
proxy = http://proxy.corp:8080
{{ end }}
[data]
name = "Jane Doe"
email = "jane@work.com"
Encrypted Files
encryption = "age"
[age]
identity = "~/.config/chezmoi/key.txt"
recipient = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"
Run Scripts
{{ if eq .chezmoi.os "darwin" -}}
brew bundle --file=/dev/stdin <<EOF
brew "ripgrep"
brew "fd"
brew "fzf"
EOF
{{ else if eq .chezmoi.os "linux" -}}
sudo apt install -y ripgrep fd-find fzf
{{ end -}}
Shell Startup File Order
Bash
Login shell: /etc/profile -> ~/.bash_profile OR ~/.bash_login OR ~/.profile (first found)
Interactive shell: ~/.bashrc
Best practice — put everything in .bashrc, source from .bash_profile:
[[ -f ~/.bashrc ]] && source ~/.bashrc
Zsh
Order: .zshenv (always) -> .zprofile (login) -> .zshrc (interactive) -> .zlogin (login)
Logout: .zlogout
Keep .zshenv minimal. Put most config in .zshrc.
Common Shell Config
HISTFILE=~/.zsh_history
HISTSIZE=50000
SAVEHIST=50000
setopt SHARE_HISTORY HIST_IGNORE_DUPS HIST_IGNORE_SPACE
typeset -U PATH path
path=("$HOME/.local/bin" "$HOME/go/bin" "$HOME/.cargo/bin" "/opt/homebrew/bin" $path)
export PATH
alias ll='ls -lAh'
alias g='git'
alias dc='docker compose'
alias k='kubectl'
alias ..='cd ..'
alias ...='cd ../..'
mkcd() { mkdir -p "$1" && cd "$1"; }
export EDITOR='nvim'
export VISUAL='nvim'
eval "$(starship init zsh)"
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
Git Configuration
[user]
name = Your Name
email = you@example.com
signingkey = ~/.ssh/id_ed25519.pub
[alias]
co = checkout
br = branch
ci = commit
st = status
sw = switch
lg = log --oneline --graph --all --decorate
unstage = reset HEAD --
amend = commit --amend --no-edit
wip = !git add -A && git commit -m 'WIP'
[core]
editor = nvim
pager = delta
autocrlf = input
excludesFile = ~/.gitignore_global
[init]
defaultBranch = main
[pull]
rebase = true
[push]
autoSetupRemote = true
[merge]
conflictstyle = zdiff3
[diff]
algorithm = histogram
colorMoved = default
[delta]
navigate = true
side-by-side = true
line-numbers = true
[gpg]
format = ssh
[commit]
gpgsign = true
[rerere]
enabled = true
# ~/.gitignore_global
.DS_Store
Thumbs.db
*.swp
*~
.idea/
.vscode/
Terminal Configs
tmux
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",xterm-256color:RGB"
set -g mouse on
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on
set -g history-limit 50000
set -g escape-time 0
unbind C-b
set -g prefix C-a
bind C-a send-prefix
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
bind r source-file ~/.tmux.conf \; display "Reloaded"
WezTerm
local wezterm = require("wezterm")
local config = wezterm.config_builder()
config.font = wezterm.font("JetBrains Mono")
config.font_size = 14.0
config.color_scheme = "Catppuccin Mocha"
config.hide_tab_bar_if_only_one_tab = true
return config
macOS-Specific
Brewfile
brew "git"
brew "neovim"
brew "ripgrep"
brew "fd"
brew "fzf"
brew "stow"
brew "starship"
brew "git-delta"
brew "jq"
brew "bat"
brew "eza"
brew "tmux"
cask "wezterm"
cask "raycast"
cask "1password"
cask "docker"
brew bundle --file=~/dotfiles/Brewfile
brew bundle dump --force --file=~/dotfiles/Brewfile
macOS Defaults
#!/bin/bash
defaults write com.apple.finder AppleShowAllFiles -bool true
defaults write com.apple.finder ShowPathbar -bool true
defaults write com.apple.dock autohide -bool true
defaults write com.apple.dock autohide-delay -float 0
defaults write NSGlobalDomain KeyRepeat -int 2
defaults write NSGlobalDomain InitialKeyRepeat -int 15
mkdir -p ~/Screenshots
defaults write com.apple.screencapture location ~/Screenshots
killall Finder Dock SystemUIServer 2>/dev/null
Linux-Specific
Xft.dpi: 144
*.foreground:
*.background:
systemd User Services
[Unit]
Description=SSH Agent
[Service]
Type=simple
ExecStart=/usr/bin/ssh-agent -D -a %t/ssh-agent.socket
[Install]
WantedBy=default.target
systemctl --user enable --now ssh-agent
XDG Base Directory Specification
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"
Tools that respect XDG: nvim, tmux (3.1+), git, starship, wezterm, alacritty, kitty, bat, ripgrep.
Secrets Management
Never commit secrets in plaintext.
brew install git-crypt
cd ~/dotfiles && git-crypt init
git-crypt export-key ~/dotfiles-key
git-crypt unlock ~/dotfiles-key
export GITHUB_TOKEN=$(op read "op://Dev/GitHub/token")
age-keygen -o ~/.config/chezmoi/key.txt
Bootstrap Script
#!/usr/bin/env bash
set -euo pipefail
DOTFILES_REPO="git@github.com:user/dotfiles.git"
DOTFILES_DIR="$HOME/dotfiles"
if [[ "$OSTYPE" == darwin* ]]; then
command -v brew >/dev/null || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"
brew install git stow
elif [[ -f /etc/debian_version ]]; then
sudo apt update && sudo apt install -y git stow
elif [[ -f /etc/arch-release ]]; then
sudo pacman -Sy --noconfirm git stow
fi
[[ ! -d "$DOTFILES_DIR" ]] && git clone "$DOTFILES_REPO" "$DOTFILES_DIR"
cd "$DOTFILES_DIR"
for dir in */; do stow -v "$dir"; done
if [[ "$OSTYPE" == darwin* ]]; then
brew bundle --file="$DOTFILES_DIR/Brewfile" || true
[[ -f "$DOTFILES_DIR/.macos" ]] && bash "$DOTFILES_DIR/.macos"
fi
echo "Bootstrap complete. Restart your shell."
Best Practices
- Version control everything — git repo, push to private GitHub/GitLab
- Modular structure — one stow package or chezmoi directory per application
- No secrets in plaintext — use chezmoi encryption, git-crypt, or 1Password CLI
- Bootstrap script — automate fresh machine setup end to end
- Test before applying — use
chezmoi diff or stow -n -v before committing
- Separate public and private — secrets in private repo, everything else public
- XDG compliance — prefer
~/.config/ over ~/ when tools support it
- Idempotent scripts — bootstrap should be safe to run multiple times