Mac Setup for Web Development
My personal macOS setup for web development — Homebrew, iTerm2, Oh My Zsh, Starship, VS Code, Git with delta, Volta for Node, and more.
This is my personal macOS setup for web development. Everything here is scripted in my dotfiles — you can run a single command to apply it all on a fresh machine.
One-Command Install
Clone the repo and run the install script:
git clone https://github.com/liambeeton/dotfiles ~/.dotfiles
cd ~/.dotfiles
./install.shOr install directly without cloning first:
curl -#L https://github.com/liambeeton/dotfiles/tarball/master | tar -xzv -C ~/.dotfiles --strip-components=1
~/.dotfiles/install.shThe script applies macOS defaults, installs Homebrew, CLI packages, GUI apps, Oh My Zsh, VS Code extensions, Git config, and Node via Volta — in order.
Run this on a fresh machine
The install script overwrites .zshrc, .gitconfig, and VS Code settings. It's designed for a clean macOS install — running it on an existing machine may clobber config you want to keep.
MacBook Specification
- MacBook Pro 16-inch (M4 Max)
- Apple M4 Max — 16-core CPU, 40-core GPU, 16-core Neural Engine
- 128 GB unified memory
- 8 TB SSD
- Liquid Retina XDR display
- QWERTY — English (International)
- macOS Sequoia
macOS System Preferences
- Appearance — Dark Mode; Show Scroll Bars → Always
- Dock — Auto-hide; smaller size; remove most apps; Show Percentage for battery
- Display — Night Shift
- Security — Touch ID; FileVault on
- Notifications — Off, except Calendar
- Siri — Disabled
- Trackpad — Tap to Click; Speed Max; Point & Click → Look up off; Notification Centre off
- Keyboard — Disable auto-capitalise, smart quotes, double-space period; Spotlight shortcut off (Raycast takes over)
- Mission Control — Hot Corners: all off
- Finder — Show all extensions; Show hidden files; New window → Downloads; remove unused sidebar items; Tags off
- Accessibility — Scroll Speed: Max
macOS Defaults (Terminal)
These are applied automatically by macos/defaults.sh:
# Screenshots as JPG instead of PNG
defaults write com.apple.screencapture type jpg
# Don't reopen PDFs when opening a new one
defaults write com.apple.Preview ApplePersistenceIgnoreState YES
# Show Library folder
chflags nohidden ~/Library
# Show hidden files
defaults write com.apple.finder AppleShowAllFiles YES
# Show path bar and status bar in Finder
defaults write com.apple.finder ShowPathbar -bool true
defaults write com.apple.finder ShowStatusBar -bool trueRun killall Finder after applying these defaults to pick up the changes immediately — no reboot needed.
Hostname
Set the machine name to Nomad:
sudo scutil --set ComputerName "Nomad"
sudo scutil --set HostName "Nomad"
sudo scutil --set LocalHostName "Nomad"Homebrew
Install Homebrew as the macOS package manager:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
brew update && brew upgradeCLI Packages
brew install \
awscli \
azure-cli \
difftastic \
direnv \
dive \
dotnet \
eza \
flyway \
git \
git-delta \
git-lfs \
go \
golangci-lint \
gotestsum \
gnupg \
gradle \
helm \
jenv \
jq \
k6 \
kubectl \
maven \
minikube \
mkcert \
ollama \
openjdk@11 \
openjdk@17 \
openjdk@21 \
openssl@3 \
ripgrep \
starship \
tailscale \
tfenv \
tgenv \
tree \
uv \
wget \
ykmanGUI Apps
brew install --cask \
1password \
arq \
bartender \
brave-browser \
charles \
claude \
claude-code \
discord \
docker-desktop \
firefox \
font-fira-code \
font-hack-nerd-font \
font-jetbrains-mono \
font-source-code-pro \
foobar2000 \
google-chrome \
imageoptim \
insomnia \
iterm2 \
jetbrains-toolbox \
little-snitch \
maccy \
ngrok \
notion \
postman \
protonvpn \
raycast \
rectangle \
slack \
soapui \
spotify \
tailscale-app \
typora \
visual-studio-code \
vlc \
whatsapp \
wireshark-app \
yubico-authenticator \
zoomGUI Applications
- 1Password — password manager; enable Touch ID
- Arq — local and cloud backups
- Bartender — menu bar management
- Raycast — Spotlight replacement; bind to CMD + Space; enable File Search, Snippets, System
- Brave — primary browser for personal browsing
- Google Chrome — web development; enable dark mode; DevTools → dark mode; Network → Fetch/XHR only
- Firefox — secondary browser for dev testing
- Charles — HTTP proxy for debugging API traffic
- Docker Desktop — containerised databases and services; enable Docker Compose
- ImageOptim — compress images before committing
- Insomnia — REST and GraphQL API testing
- iTerm2 — terminal (see iTerm2 section)
- JetBrains Toolbox — manage JetBrains IDEs
- Little Snitch — outbound firewall
- Maccy — clipboard manager; enable Launch at Login
- Notion — notes and project management
- Postman — API testing
- ProtonVPN — VPN
- Raycast — launcher
- Rectangle — window management shortcuts
- Slack — team messaging
- Typora — minimal Markdown editor
- Visual Studio Code — primary editor (see VS Code section)
- VLC — video player; set as default
- Wireshark — network packet analysis
- Yubico Authenticator — hardware 2FA with YubiKey
- Zoom — video calls
Raycast shortcut
Before binding Raycast to ⌘ Space, disable the Spotlight shortcut first: System Preferences → Keyboard → Keyboard Shortcuts → Spotlight → uncheck "Show Spotlight search".
iTerm2
Configuration for a clean, fullscreen terminal experience:
- Preferences → General → Window
- Unselect "Native full screen windows"
- Select "Close windows when closing an app"
- Appearance → Windows → Hide scrollbars
- Tabs → unselect "Show tab bar in fullscreen"
- Dimming → unselect all
- Profiles → Window: Transparency 30; Style: Full Screen; Screen: Main Screen
- Profiles → Advanced: Semantic History → Open with VS Code; Natural Text Editing
- Profiles → Text → Font: Hack Nerd Font Mono
- Bring to fullscreen:
⌘ + Enter
Oh My Zsh
Install Oh My Zsh and required plugins:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
# Plugins
git clone https://github.com/zsh-users/zsh-autosuggestions \
${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
git clone https://github.com/zsh-users/zsh-completions \
${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-completions
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git \
${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
# z — jump to frequently used directories
wget https://raw.githubusercontent.com/rupa/z/master/z.sh -O ~/z.shStarship Theme
Starship must be the last line in .zshrc. The config below already has eval "$(starship init zsh)" at the end — don't move it.
Install Starship via Homebrew (already in the brew list above), then activate it:
echo 'eval "$(starship init zsh)"' >> ~/.zshrc.zshrc
export ZSH="$HOME/.oh-my-zsh"
plugins=(
direnv git kubectl pyenv z
zsh-autosuggestions zsh-syntax-highlighting
)
fpath+=${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-completions/src
source $ZSH/oh-my-zsh.sh
# Navigation
alias dev="cd ~/Developer"
alias dot="cd ~/.dotfiles"
# System
alias ip="ipconfig getifaddr en0"
alias hostsconfig="sudo nano /etc/hosts"
alias loc="npx sloc --format cli-table --format-option head --exclude 'build|\.svg$\.xml' ./"
# Zsh config
alias zshconfig="code ~/.zshrc"
alias zshsource="source ~/.zshrc"
alias ohmyzsh="cd ~/.oh-my-zsh"
# SSH
alias sshhome="cd ~/.ssh"
alias sshconfig="code ~/.ssh/config"
# Git
alias gitconfig="code ~/.gitconfig"
alias gits="git status"
alias gitd="git diff"
alias gitl="git lg"
alias gita="git add ."
alias gitc="cz commit"
alias gitf='git commit --fixup $(git log -1 --format=%H)'
alias gitn='git branch | grep -v "develop" | grep -v "master" | grep -v "main" | xargs git branch -D'
# Docker
alias docv="docker ps -a"
alias docu="docker-compose up --build"
alias docb="docker-compose build"
alias docd="docker-compose down -v"
alias docs='docker stop $(docker ps -a -q)'
alias docr='docker rm $(docker ps -a -q)'
source ~/z.sh
# PATH exports
export DOTNET_ROOT=$HOME/.dotnet
export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools
export GOPATH="$HOME/go"
export GOROOT="$(brew --prefix golang)/libexec"
export PATH="$PATH:$GOPATH/bin:$GOROOT/bin"
export PATH="$HOME/.jenv/bin:$PATH"
export PATH="$HOME/.jenv/shims:$PATH"
eval "$(jenv init -)"
# pnpm
export PNPM_HOME="/Users/liam/Library/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;
esac
eval "$(direnv hook zsh)"
eval "$(starship init zsh)" # must be lastAfter any change to .zshrc:
source ~/.zshrcVS Code
Extensions
Install all extensions automatically:
cat ~/.dotfiles/code/extensions.sh | bashExtensions included:
- Anthropic Claude Code
- Auto Hide
- Code Spell Checker
- Docker
- ESLint
- ES7+ React/Redux Snippets
- GitHub Dark Default
- Go
- HCL
- HCL Format
- markdownlint
- MDX
- .NET C# Dev Kit
- Prettier
- Pretty TypeScript Errors
- React Refactor
- Simple React Snippets
- Status Bar Format Toggle
- Terraform
- TypeScript Nightly
- vscode-icons
- YAML
Keybindings
[
{ "key": "ctrl+up", "command": "cursorMove", "args": { "to": "up", "by": "line", "value": 10 } },
{ "key": "ctrl+down", "command": "cursorMove", "args": { "to": "down", "by": "line", "value": 10 } }
]Settings
{
"workbench.colorTheme": "GitHub Dark Default",
"workbench.iconTheme": "vscode-icons",
"workbench.sideBar.location": "right",
"workbench.activityBar.location": "hidden",
"workbench.statusBar.visible": true,
"workbench.startupEditor": "none",
"workbench.editor.showTabs": "none",
"workbench.editor.enablePreview": false,
"editor.fontFamily": "Hack Nerd Font Mono",
"editor.fontSize": 14,
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.detectIndentation": false,
"editor.minimap.enabled": false,
"editor.stickyScroll.enabled": false,
"editor.renderWhitespace": "none",
"editor.formatOnSave": false,
"editor.inlineSuggest.enabled": true,
"editor.lineNumbers": "on",
"workbench.colorCustomizations": {
"editor.lineHighlightBackground": "#102032"
},
"files.associations": {
".env*": "makefile"
},
"editor.tokenColorCustomizations": {
"textMateRules": [
{
"scope": "string.quoted.double.env, string.quoted.single.env",
"settings": { "foreground": "#00000000" }
}
]
},
"[javascript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[astro]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"cSpell.enableFiletypes": ["mdx"]
}Git
Git config is symlinked from ~/.dotfiles/git/.gitconfig:
ln -sfv ~/.dotfiles/git/.gitconfig ~Key configuration:
# Identity
git config --global user.name "Liam Beeton"
git config --global user.email "liam.beeton@gmail.com"
# Editor
git config --global core.editor "code --wait"
# Default branch
git config --global init.defaultBranch main
# Better diff pager (git-delta)
git config --global core.pager "delta"
git config --global delta.side-by-side true
git config --global delta.line-numbers true
git config --global delta.decorations true
# Use VS Code for diffs and merges
git config --global diff.tool vscode
git config --global merge.tool vscode
# Useful aliases
git config --global alias.st "status"
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"Commit Message Template
The commit template at ~/.gitmessage.txt enforces a consistent format:
- Imperative mood in subject line (e.g. "Add feature" not "Added feature")
- 72-character line limit
- Separate subject from body with a blank line
- Reference issues or tickets in the footer
SSH
One SSH key per service. Example for GitHub:
Save your passphrase first
When prompted, set a strong passphrase and save it in 1Password immediately. You'll need it whenever the SSH agent restarts (e.g. after a reboot).
mkdir -p ~/.ssh
cd ~/.ssh
# Generate key
ssh-keygen -t ed25519 -C "github"
# File name: github
# Set a passphrase and store it in 1PasswordVerify the passphrase:
ssh-keygen -y -f githubAdd to ~/.ssh/config:
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/githubAdd to macOS keychain:
ssh-add --apple-use-keychain ~/.ssh/githubAdd the public key to GitHub:
cat ~/.ssh/github.pub | pbcopy
# Paste at https://github.com/settings/ssh/new
# Or via GitHub CLI
gh auth login
gh ssh-key add ~/.ssh/github.pub -t githubNode.js via Volta
Volta pins Node versions per project and manages global tools cleanly:
# Install Volta
curl https://get.volta.sh | bash
source ~/.zshenv
# Install latest LTS Node
volta install nodeCheck versions:
node -v && npm -vInstall global npm tools:
npm install -g \
eslint \
http-server \
nodemon \
pnpm \
release-it \
typescript \
yarnPin a Node version to a specific project:
cd my-project
volta pin node@22This writes a volta field to package.json so anyone on the project gets the same Node version automatically.
Why Volta over nvm?
Volta switches Node versions automatically when you cd into a project — no nvm use needed. The pinned version is stored in package.json so the whole team stays in sync without any extra setup.
Source: github.com/liambeeton/dotfiles