New machine? Reinstall? Your perfect dev environment should be one command away. Here’s how to manage dotfiles properly.
The Problem# You spend hours configuring:
Shell (zsh, bash) Editor (vim, nvim, VS Code) Git config SSH config Tmux Aliases and functions Then you get a new laptop and do it all again. Badly.
The Basic Solution# Put dotfiles in a Git repo, symlink them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Create repo
mkdir ~/dotfiles
cd ~/dotfiles
git init
# Move configs
mv ~/.zshrc ~/dotfiles/zshrc
mv ~/.vimrc ~/dotfiles/vimrc
mv ~/.gitconfig ~/dotfiles/gitconfig
# Create symlinks
ln -sf ~/dotfiles/zshrc ~/.zshrc
ln -sf ~/dotfiles/vimrc ~/.vimrc
ln -sf ~/dotfiles/gitconfig ~/.gitconfig
# Push to GitHub
git remote add origin git@github.com:username/dotfiles.git
git push -u origin main
Stow: Symlink Manager# GNU Stow makes symlinks manageable:
d ├ │ ├ │ ├ │ └ o ─ ─ ─ ─ t ─ ─ ─ ─ f i z └ v └ g └ t └ l s ─ i ─ i ─ m ─ e h ─ m ─ t ─ u ─ s x . . / . z v g t s i i m h m t u r r c x c c o . n c f o i n g f 1
2
3
4
5
6
7
8
cd ~/dotfiles
stow zsh # Creates ~/.zshrc → ~/dotfiles/zsh/.zshrc
stow vim
stow git
stow tmux
# Remove symlinks
stow -D zsh
Installation Script# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/bin/bash
# install.sh
set -e
DOTFILES = " $HOME /dotfiles"
# Clone if not exists
if [ ! -d " $DOTFILES " ] ; then
git clone https://github.com/username/dotfiles.git " $DOTFILES "
fi
cd " $DOTFILES "
# Install packages
if command -v apt & > /dev/null; then
sudo apt update
sudo apt install -y zsh vim tmux stow
elif command -v brew & > /dev/null; then
brew install zsh vim tmux stow
fi
# Stow everything
for dir in */; do
stow " $dir "
done
# Change shell
chsh -s $( which zsh)
echo "Done! Restart your terminal."
Handling Sensitive Data# Don’t commit secrets.
Option 1: Separate File# 1
2
3
4
5
6
# .zshrc
source ~/.secrets # Not in repo
# .secrets (gitignored)
export API_KEY = "xxx"
export DB_PASSWORD = "yyy"
Option 2: Git-crypt# 1
2
git-crypt init
echo "secrets/** filter=git-crypt diff=git-crypt" >> .gitattributes
Files matching the pattern are encrypted in the repo.
Option 3: Environment Variables# Load from password manager or secrets service at runtime.
Machine-Specific Configs# Conditional in Shell# 1
2
3
4
5
6
7
8
9
10
11
12
13
# .zshrc
if [[ " $OSTYPE " == "darwin" * ]] ; then
# macOS
alias ls = 'ls -G'
export PATH = "/opt/homebrew/bin: $PATH "
else
# Linux
alias ls = 'ls --color=auto'
fi
if [[ " $( hostname) " == "work-laptop" ]] ; then
export HTTP_PROXY = "http://proxy.corp:8080"
fi
Separate Stow Packages# d ├ ├ ├ └ o ─ ─ ─ ─ t ─ ─ ─ ─ f i z z z z l s s s s e h h h h s - - - m l w a i o c n r / u k x # # # s s s t t t o o o w w w o o o n n n l l l y y y o o a n n t m l w a i o c n r u k x 1
2
3
stow zsh
[[ " $OSTYPE " == "darwin" * ]] && stow zsh-mac
[[ " $OSTYPE " == "linux" * ]] && stow zsh-linux
Essential Configs# Git# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# .gitconfig
[user]
name = Your Name
email = you@example.com
[core]
editor = vim
excludesfile = ~/.gitignore_global
[alias]
st = status
co = checkout
br = branch
ci = commit
lg = log --oneline --graph --decorate
[pull]
rebase = true
[init]
defaultBranch = main
Shell Aliases# 1
2
3
4
5
6
7
8
9
10
11
12
# .zshrc or .bashrc
alias ll = 'ls -la'
alias ..= 'cd ..'
alias ...= 'cd ../..'
alias g = 'git'
alias k = 'kubectl'
alias d = 'docker'
alias dc = 'docker compose'
# Quick edits
alias zshrc = '$EDITOR ~/.zshrc && source ~/.zshrc'
alias vimrc = '$EDITOR ~/.vimrc'
SSH Config# # H H H o o o . s s s s t t t s A I H U I H U I F h d d g o s d p o s d o d e i s e e r s e e r c K n t t r n o t r n w o e t h N t d N t a n y i u a g i a d i r f s t b m i t m e t d i T i . e t y e p y A g o e c F l F g A s o g i 1 o i e g O m i l 9 y l n e n t e 2 e t n l h . t y u ~ 1 ~ y b / 6 / e y y . . 8 . s e e c s . s s s o s 1 s m h . h / 1 / g 0 p i 0 r t o h d u b Tmux# # s u b s s s # b b # b e n i e e e i i i . t b n t t t S n n R n t i d p d d e d m - n - - - l l u g d C g g g i | - o r x - t a . p C a m b h s s d s c r - o a i p p p o o e b s u s s a l l c u n f e s e t n i i o r f i n e - o e t t n c x d i r s - - f e - o n y w w i - C p n d - i i g f - r e l n n i a e x i d d l f m o o e i 1 i w w x t ~ - / 1 h . 0 t 0 m 0 u 0 x . c o n f \ ; d i s p l a y " R e l o a d e d ! " Bootstrap Script Example# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/bash
# bootstrap.sh - One command setup
set -euo pipefail
REPO = "https://github.com/username/dotfiles.git"
DOTFILES = " $HOME /dotfiles"
echo "=== Dotfiles Bootstrap ==="
# Detect OS
if [[ " $OSTYPE " == "darwin" * ]] ; then
OS = "mac"
# Install Homebrew
if ! command -v brew & > /dev/null; then
/bin/bash -c " $( curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh) "
fi
brew install git stow zsh vim tmux fzf ripgrep
else
OS = "linux"
sudo apt update
sudo apt install -y git stow zsh vim tmux fzf ripgrep
fi
# Clone dotfiles
if [ -d " $DOTFILES " ] ; then
cd " $DOTFILES " && git pull
else
git clone " $REPO " " $DOTFILES "
fi
cd " $DOTFILES "
# Backup existing configs
backup_dir = " $HOME /.dotfiles_backup/ $( date +%Y%m%d_%H%M%S) "
mkdir -p " $backup_dir "
for file in .zshrc .vimrc .gitconfig .tmux.conf; do
if [ -f " $HOME / $file " ] && [ ! -L " $HOME / $file " ] ; then
mv " $HOME / $file " " $backup_dir /"
echo "Backed up $file "
fi
done
# Stow packages
stow zsh vim git tmux
[ " $OS " = "mac" ] && stow mac
[ " $OS " = "linux" ] && stow linux
# Install vim plugins
if [ -f ~/.vimrc ] ; then
vim +PlugInstall +qall 2>/dev/null || true
fi
# Set shell
if [ " $SHELL " != " $( which zsh) " ] ; then
chsh -s " $( which zsh) "
fi
echo "=== Bootstrap Complete ==="
echo "Restart your terminal or run: exec zsh"
Run on any new machine:
1
curl -fsSL https://raw.githubusercontent.com/username/dotfiles/main/bootstrap.sh | bash
The Dotfiles Checklist# Popular Dotfile Repos to Study# mathiasbynens/dotfiles (macOS focused) holman/dotfiles (organized, well documented) thoughtbot/dotfiles (clean, minimal) Look at how others organize, then build your own.
Your dev environment is an extension of how you think. Version it, automate it, never rebuild it from memory again.