How do I set up my Mac (dotfiles-first)

This post is a living checklist for how I set up a new macOS machine for development. The source of truth is my dotfiles repo; this page is here to explain the flow and the “why”, not to become another set of instructions I forget to update.

  • Dotfiles: https://github.com/mehuaniket/dotfiles
  • Neovim config (vendored as a submodule inside dotfiles): https://github.com/mehuaniket/kickstart.nvim

The setup philosophy (dotfiles-first)

I try to keep the setup reproducible and idempotent:

  • Homebrew Bundle: for apps + CLI tools via a Brewfile
  • GNU stow: for symlinking config into $HOME (no copy/paste)
  • A single setup script: to glue everything together

If you only read one thing, read ~/.dotfiles/scripts/setup.sh. That’s what I actually run.

Before you start (5 minutes that saves hours)

  • Install Xcode Command Line Tools (git/make/etc.):
xcode-select --install
  • Decide if you want “dotfiles to win”:
    • If you already have ~/.zshrc, ~/.config/nvim, or ~/.config/tmux, stow may conflict (and the script will warn).
    • I usually back up old configs first (rename the old directories/files), then run the script.

Safety note (about running remote scripts)

The one-liner uses curl to download and run a script. I do it for convenience on new machines, but:

  • Inspect it first: ~/.dotfiles/scripts/setup.sh (or the same file in the GitHub repo)
  • Prefer the local clone approach if you want maximum transparency

Clone dotfiles and run the setup script

This is the “use my dotfiles” path (local, explicit, easy to inspect):

git clone --recurse-submodules https://github.com/mehuaniket/dotfiles ~/.dotfiles
bash ~/.dotfiles/scripts/setup.sh --brew

If Homebrew packages are already installed, you can omit --brew:

bash ~/.dotfiles/scripts/setup.sh

One-liner variant (same script, remote)

If you’re on a fresh machine and want to run the same setup script directly:

zsh -c "$(curl -fsSL https://raw.githubusercontent.com/mehuaniket/dotfiles/main/scripts/setup.sh)" -- --brew

Note the -- --brew: the first -- separates zsh -c args from the script args.

What the setup script does

The script (~/.dotfiles/scripts/setup.sh) is opinionated, but it’s pretty straightforward:

  • Homebrew (optional):
    • Installs Homebrew if missing
    • Runs brew bundle --file="$HOME/.dotfiles/homebrew/Brewfile" (reproducible installs)
    • Ensures a few key formulas/casks exist (things like fzf, fd, ripgrep, tmux, neovim, kubectl, terraform, gh, nerd fonts, stow, etc.)
  • Zsh:
    • Installs Oh My Zsh (if missing)
    • Installs plugins:
      • zsh-autosuggestions
      • zsh-syntax-highlighting
    • Installs Powerlevel10k
    • Tries to set your login shell to Zsh (chsh) if needed
  • tmux:
    • Installs TPM (tmux plugin manager) into ~/.config/tmux/plugins/tpm
    • Sources tmux config and installs plugins
  • Dotfiles linking (stow):
    • Runs stow for a default set of modules: zsh, tmux, fzf, nvim
    • Uses .stowrc (target is ~/ and ignores things like scripts/, homebrew/, misc/, etc.)

Where the configs land (what gets symlinked)

These are the important “resulting paths” after stow runs:

  • Zsh: ~/.zshrc, ~/.p10k.zsh
  • tmux: ~/.config/tmux/tmux.conf
  • fzf/fd: ~/.fdignore
  • Neovim: ~/.config/nvim/

Manual steps (still required)

There are a few things I keep manual because they’re UI-driven:

  • iTerm profile: import ~/.dotfiles/misc/iterm_default_profile.json
    • iTerm2 → Settings → Profiles → Other Actions → Import JSON Profiles
  • (Optional) iTerm colors: import ~/.dotfiles/misc/catppuccin_mocha.itermcolors
  • Restart your terminal
  • Open Neovim once so plugins can install/sync:
nvim

If you want to be explicit inside Neovim: run :Lazy and then :Lazy sync.

Optional dotfiles modules (things you may also want)

The setup script stows zsh, tmux, fzf, nvim. I also keep other modules in the repo that you can opt into:

cd ~/.dotfiles
stow git k9s hammerspoon delta
  • git/: git config + commit message template
  • k9s/: my k9s config + skins
  • hammerspoon/: window management / hotkeys via Hammerspoon
  • delta/: terminal theming bits (e.g. bat themes)

Day-2 operations (upgrades / reruns)

Updating the repo:

cd ~/.dotfiles
git pull --recurse-submodules

Re-running the setup (safe to re-run):

bash ~/.dotfiles/scripts/setup.sh

If you want to re-apply Homebrew bundle too:

bash ~/.dotfiles/scripts/setup.sh --brew

Or run brew bundle directly:

brew bundle --file=~/.dotfiles/homebrew/Brewfile && brew cleanup

Cheatsheet (the stuff I actually use daily)

tmux

  • Prefix: Ctrl + Space
  • New window: Prefix + c
  • Split:
    • Prefix + " (horizontal)
    • Prefix + % (vertical)
  • Move between panes (vim-style): Prefix + h/j/k/l
  • Move between panes (no prefix): Alt + Arrow keys
  • Switch windows (no prefix): Shift + Left/Right

If you ever get lost, re-source the config:

tmux source-file ~/.config/tmux/tmux.conf

Neovim

  • Leader key: Space
  • Move between splits: Ctrl + h/j/k/l
  • Search files: <leader>sf
  • Search keymaps: <leader>sk
  • Search in current buffer: <leader>/
  • Search Neovim config: <leader>sn

Debugger (DAP):

  • Start/Continue: F5
  • Step into/over/out: F1 / F2 / F3
  • Toggle breakpoint: <leader>b
  • Toggle DAP UI: F7

Troubleshooting (common gotchas)

stow conflicts

If the script says stow failed for a target (like zsh or nvim), you probably already have files at the destination path.

  • For example, zsh wants to manage ~/.zshrc
  • nvim wants to manage ~/.config/nvim/

Fix: move the old file/dir out of the way and re-run the script.

Homebrew shellenv not applied

If brew installs but your shell can’t find it, restart the terminal once, or run:

eval "$(/opt/homebrew/bin/brew shellenv)"

tmux plugins didn’t install

Inside tmux, try re-sourcing the config:

tmux source-file ~/.config/tmux/tmux.conf