Managing Dotfiles Like a Pro
Every developer I know has spent an afternoon setting up a new machine, only to realise they cannot remember how they configured their shell, what Git aliases they relied on, or which Vim plugins made their editor actually usable. Then they do it again six months later when they get a new laptop.
Managing your dotfiles properly solves this problem permanently. I have maintained my own dotfiles repository for over eight years now, and a well-maintained dotfiles repository means you can go from a fresh machine to a fully configured development environment in minutes, not hours.
What Makes Dotfiles Worth Managing
Your dotfiles represent years of accumulated preferences, shortcuts, and optimisations. They are the difference between a generic terminal and one that feels like yours. Here are the files that matter most:
Shell configuration (.bashrc, .zshrc, .bash_profile): Your aliases, functions, PATH modifications, prompt customisation, and shell options. This is where most of your productivity shortcuts live.
Git configuration (.gitconfig): Your identity, aliases, diff and merge tool preferences, default branch name, and signing configuration. A well-configured .gitconfig saves minutes every day. Good commit message habits pair well with the right Git aliases.
Editor configuration (.vimrc, VS Code settings.json): Keybindings, formatting rules, plugin lists, and theme preferences. If you use VS Code, our guide to VS Code extensions that will change how you code covers some essentials worth including in your configuration.
SSH configuration (.ssh/config): Host aliases, jump hosts, key specifications, and connection options that save you from typing long SSH commands.
Terminal emulator configuration (.tmux.conf, .alacritty.yml): Window management, keybindings, colours, and fonts.
Dotfiles Management Tools Compared
| Tool | Approach | Templates | Secrets Management | Learning Curve | Best For |
|---|---|---|---|---|---|
| GNU Stow | Symlink farm manager | No | No | Low | Simple setups, Unix purists |
| chezmoi | Dedicated dotfiles manager | Yes | Yes (1Password, Bitwarden) | Medium | Complex, multi-machine setups |
| yadm | Git wrapper | Yes (Jinja2) | Yes (GPG encryption) | Low | Git-comfortable developers |
| Bare Git repo | Raw Git in home directory | No | No | Medium | Minimalists, no extra tools |
| Ansible | Full config management | Yes (Jinja2) | Yes (Ansible Vault) | High | System-wide provisioning |
Setting Up a Dotfiles Repository
The core idea is simple: store your dotfiles in a Git repository and create symbolic links from the repository to the locations where your tools expect them.
The Basic Approach
Create a repository and move your dotfiles into it:
mkdir ~/dotfiles
cd ~/dotfiles
git init
# Move files into the repo
mv ~/.gitconfig ~/dotfiles/gitconfig
mv ~/.zshrc ~/dotfiles/zshrc
# Create symlinks back to the expected locations
ln -sf ~/dotfiles/gitconfig ~/.gitconfig
ln -sf ~/dotfiles/zshrc ~/.zshrc
This works, but managing symlinks manually becomes tedious as your collection grows.
Using GNU Stow
GNU Stow ↗ automates symlink management elegantly. It treats each subdirectory in your dotfiles repo as a “package” and creates symlinks that mirror the directory structure.
~/dotfiles/
git/
.gitconfig
zsh/
.zshrc
.zprofile
vim/
.vimrc
.vim/
colors/
plugin/
tmux/
.tmux.conf
To install all your git dotfiles:
cd ~/dotfiles
stow git # Creates ~/.gitconfig → ~/dotfiles/git/.gitconfig
stow zsh # Creates ~/.zshrc → ~/dotfiles/zsh/.zshrc
stow vim # Creates ~/.vimrc → ~/dotfiles/vim/.vimrc (and subdirectories)
Stow handles nested directories correctly and can unstow (remove symlinks) just as easily.
Using Chezmoi
For more complex setups, chezmoi ↗ is purpose-built for dotfiles management. It supports templates, secrets management, and machine-specific configuration out of the box.
chezmoi init
chezmoi add ~/.zshrc
chezmoi add ~/.gitconfig
# Edit a managed file
chezmoi edit ~/.zshrc
# Apply changes
chezmoi apply
Chezmoi’s templating is particularly useful for files that need to differ between machines:
[user]
name = Gareth Clubb
email = {{ if eq .chezmoi.hostname "work-laptop" }}gareth@company.com{{ else }}gareth@personal.com{{ end }}
Writing a Bootstrap Script
A bootstrap script automates the entire setup process. When you get a new machine, you clone your repo and run one command. In my experience, the time you invest writing a solid bootstrap script pays for itself many times over.
#!/bin/bash
set -euo pipefail
DOTFILES_DIR="$HOME/dotfiles"
# Install essential packages
if [[ "$(uname)" == "Darwin" ]]; then
# macOS
if ! command -v brew &>/dev/null; then
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
brew bundle --file="$DOTFILES_DIR/Brewfile"
elif [[ -f /etc/debian_version ]]; then
# Debian/Ubuntu
sudo apt update
sudo apt install -y git stow tmux neovim ripgrep fzf
fi
# Create symlinks
cd "$DOTFILES_DIR"
for dir in git zsh vim tmux; do
stow -R "$dir"
done
echo "Dotfiles installed successfully."
Brewfile for macOS
If you use macOS, a Brewfile declaratively lists all your packages:
# CLI tools
brew "git"
brew "tmux"
brew "neovim"
brew "ripgrep"
brew "fzf"
brew "jq"
brew "gh"
# Applications
cask "alacritty"
cask "visual-studio-code"
cask "firefox"
Run brew bundle to install everything. Run brew bundle dump to generate a Brewfile from your currently installed packages.
Organising Your Shell Configuration
As your shell configuration grows, a single .zshrc file becomes unwieldy. Split it into focused, sourced files:
zsh/
.zshrc # Main file that sources everything else
.zsh/
aliases.zsh # All aliases
functions.zsh # Custom functions
exports.zsh # Environment variables
path.zsh # PATH modifications
prompt.zsh # Prompt configuration
local.zsh # Machine-specific (gitignored)
Your .zshrc simply sources each file:
for config_file in ~/.zsh/*.zsh; do
source "$config_file"
done
This structure makes it easy to find and modify specific configurations without scrolling through a massive file. For more terminal tools that boost your productivity, a well-organised shell configuration is the foundation.
Handling Secrets Safely
Never commit secrets to your dotfiles repository. This includes API keys, tokens, passwords, and SSH private keys.
Environment variables. Store secrets in a file like ~/.env.local that is excluded from your repository via .gitignore. Source it from your shell configuration:
[[ -f ~/.env.local ]] && source ~/.env.local
SSH keys. Keep your .ssh/config in the repository (it contains no secrets), but never commit private keys. Document which keys need to be generated or transferred as part of your bootstrap process.
Git credential helpers. Use your OS’s credential manager (osxkeychain on macOS, libsecret on Linux) rather than storing credentials in your .gitconfig.
Essential Git Configuration
A few .gitconfig entries that pay for themselves daily:
[alias]
s = status --short
lg = log --oneline --graph --decorate -20
amend = commit --amend --no-edit
undo = reset HEAD~1 --mixed
[pull]
rebase = true
[init]
defaultBranch = main
[diff]
algorithm = histogram
colorMoved = default
[merge]
conflictstyle = zdiff3
[rerere]
enabled = true
The rerere setting is especially valuable: it records how you resolved merge conflicts and automatically applies the same resolution if the same conflict appears again. If you are working with a team, this pairs well with a solid Git workflow for smoother collaboration.
Keeping Dotfiles in Sync
If you use multiple machines, keep your dotfiles synchronised:
- Push changes from your primary machine regularly
- Pull on other machines before starting work
- Use the bootstrap script’s symlink step (
stow -R) to apply any structural changes
Consider adding a shell function that automates the sync:
dotfiles-sync() {
cd ~/dotfiles
git pull --rebase
stow -R git zsh vim tmux
echo "Dotfiles synced."
cd -
}
Getting Started Today
You do not need to do everything at once. Start with the files that matter most to you, likely your shell configuration and .gitconfig. Move them into a repository, create symlinks, and push to GitHub.
Over time, add more files as you customise your environment. The investment compounds: every configuration change you make is preserved, documented, and portable. If you enjoy automating your development environment, dotfiles management is the natural starting point. Six months from now, when you get a new machine, you will be productive in minutes instead of spending another afternoon trying to remember how you had things set up.
Frequently asked questions
What are dotfiles?
Dotfiles are configuration files on Unix-like systems that start with a dot (period), such as .bashrc, .gitconfig, and .vimrc. The dot prefix makes them hidden by default in file managers and directory listings. They control the behaviour of your shell, editor, git, and other developer tools.
Should I store dotfiles in a public GitHub repository?
Public repositories are fine for most dotfiles, but never commit secrets, API keys, SSH private keys, or tokens. Use a separate mechanism for sensitive values, such as environment variables loaded from a file excluded via .gitignore, or a secrets manager.
What is the best way to manage dotfiles?
The most common approach is a dedicated Git repository with a bootstrap script that creates symbolic links from the repo to the expected locations in your home directory. Tools like GNU Stow, chezmoi, and yadm simplify this process.
How do I handle dotfiles that differ between machines?
Use conditional logic in your shell configuration to detect the OS or hostname and apply machine-specific settings. Alternatively, tools like chezmoi support templates that generate different configurations based on the machine's properties.
What dotfiles should every developer manage?
At minimum, manage your shell configuration (.bashrc or .zshrc), Git configuration (.gitconfig), editor configuration (VS Code settings.json, .vimrc, etc.), and SSH configuration (.ssh/config). These are the files that have the biggest impact on your daily workflow.
Enjoyed this article? Get more developer tips straight to your inbox.
Comments
Join the conversation. Share your experience or ask a question below.
No comments yet. Be the first to share your thoughts.