Skip to content

macOS Setup

Personal macOS dotfiles and setup scripts powered by Homebrew, proto, and Oh My Zsh.

Prerequisites

The init script checks for both prerequisites at startup and offers to install them automatically.

PrerequisiteCheckInstall
Xcode Command Line Toolsxcode-select -pxcode-select --install
Homebrewbrew --versionbrew.sh

Installation

Remote (fresh machine)

sh
# Full install — all profiles, dotfiles, completions
bash <(curl -fsSL https://raw.githubusercontent.com/KevinDeBenedetti/dotfiles/main/os/macos/init.sh) -a

The script clones the repository to ~/.dotfiles if not already present, then re-executes from there. Using a permanent path ensures symlinks remain valid after reboot.

Local

sh
# Clone
git clone https://github.com/KevinDeBenedetti/dotfiles.git ~/.dotfiles
cd ~/.dotfiles

# Full install
./os/macos/init.sh -a

# Dotfiles only
./os/macos/init.sh -d

# Specific profiles + completions
./os/macos/init.sh -p "base,python" -c

Flags

FlagDescription
-aFull install: all profiles + dotfiles + completions + cleanup
-p <profiles>Comma-separated list of profiles (e.g. base,javascript)
-dLink dotfiles into $HOME
-cInstall zsh CLI completions
-lLite mode — skip optional/heavy packages
-rRemove bootstrap temp directory after install
-hPrint help

At least one flag is required. Running without flags prints help and exits.

Profiles

base

Core CLI tools installed via Homebrew. Split into lite (always) and additional (full mode only).

Lite:

ToolDescription
fzfFuzzy finder for shell history, files, and more
protoMulti-language toolchain version manager
sshsInteractive SSH host selector (reads ~/.ssh/config)
cheatCommunity-driven command cheatsheets
yqPortable YAML/JSON/TOML processor
treeDirectory tree visualizer
watchRepeat a command at intervals
rsyncIncremental file transfer
docker + docker-composeContainer build and orchestration CLI

Additional (full mode):

ToolDescription
ghGitHub CLI — PRs, issues, repos from the terminal
lazygitTerminal UI for Git
lazydockerTerminal UI for Docker
colimaLightweight container runtime for macOS (Docker Desktop alternative)
limaLinux VMs on macOS — used for local Debian setup testing
nmapNetwork exploration and port scanning
BrowsersBrave, Firefox, Arc
AppsInsomnia, Mattermost, OpenVPN Connect

javascript

Installs Node.js and package managers via proto.

Lite:

ToolVersion manager
nodeproto
npmproto

Additional (full mode):

ToolDescription
bunFast all-in-one JS runtime and bundler
pnpmEfficient disk-space-saving package manager
yarnAlternative npm-compatible package manager
@antfu/niUniversal package manager wrapper (ni, nr, nun…)

python

Installs Python via proto and tooling via uv.

Lite:

ToolVersion manager
pythonproto
uvBlazing-fast Python package and project manager

Additional (full mode):

ToolDescription
ruffExtremely fast Python linter and formatter
ipythonEnhanced interactive Python shell
httpieUser-friendly HTTP client for the terminal

ai

Full mode only.

ToolDescription
ollamaRun large language models locally
copilot-cliGitHub Copilot for the CLI

extras

Personal applications.

Lite: VLC

Additional (full mode): Audacity, Discord, Spotify, Transmission, Raspberry Pi Imager, Soulseek, Macs Fan Control, Radio Silence, kDrive

Dotfiles Linking

Running -d symlinks (or copies) config files from the repository into $HOME. Any existing file is backed up with a datestamp (e.g. ~/.zshrc.bak.20260311) before being replaced.

SourceTargetMethod
config/zsh/.zshrc~/.zshrccopy¹
config/git/.gitconfig~/.gitconfigsymlink
config/proto/.prototools~/.proto/.prototoolssymlink
config/oh-my-zsh/*.zsh-theme~/.oh-my-zsh/custom/themes/symlink
config/shell/*~/.config/dotfiles/symlink
config/vscode/settings.json~/Library/Application Support/Code/User/settings.jsonsymlink
config/vscode/mcp.json~/Library/Application Support/Code/User/mcp.jsonsymlink

¹ .zshrc is copied rather than symlinked because the init script applies macOS-specific patches: gsed alias and Homebrew paths for Apple Silicon (/opt/homebrew).

Completions

Running -c installs zsh completions for the following tools (placed in $(brew --prefix)/share/zsh/site-functions):

ToolSource
fzfHomebrew integration (--completion --key-bindings)
ghgh completion -s zsh
protoproto completions --shell zsh
uvuv generate-shell-completion zsh

VM Testing (Debian 13 / trixie)

Use Lima to spin up a local Debian 13 (trixie) VM and test the Debian setup scripts without a remote server.

Prerequisites

sh
brew install lima

Makefile targets

TargetDescription
make vm-createCreate and start the Debian 13 VM
make vm-installRun the full dotfiles install inside the VM
make vm-testVerify packages, SSH hardening, UFW, AppArmor, etc.
make vm-shellOpen an interactive shell in the VM
make vm-statusShow Lima instance status
make vm-stopStop the VM (preserve disk)
make vm-startStart a stopped VM
make vm-cleanDelete the VM and free disk space
make vm-fullFull cycle: create + install + verify
make vm-resetFull reset: delete, recreate, install, verify
make vm-lima-listList all Lima instances

Quick start

sh
# One-shot: create VM, install dotfiles, run verification
make vm-full

# Connect to VM shell
make vm-shell

# Teardown
make vm-clean

The Lima config is at tests/lima/debian-trixie.yaml. It uses Debian 13 (trixie) daily cloud images and Apple Virtualization framework (vz) with Rosetta for best performance on Apple Silicon.

Zsh Configuration

The .zshrc at config/zsh/.zshrc is copied (not symlinked) to ~/.zshrc so that macOS-specific patches can be applied without affecting the tracked file.

History

SettingValueEffect
HISTSIZE10000Number of commands kept in memory per session
SAVEHIST10000Number of commands persisted to ~/.zsh_history
HIST_STAMPSyyyy-mm-ddDate prefix on every history entry
HISTFILE~/.zsh_historyPersistent history file location

Theme

A custom Oh My Zsh theme kevin-de-benedetti is linked from config/oh-my-zsh/ into ~/.oh-my-zsh/custom/themes/ and set via ZSH_THEME.

Plugins

The following Oh My Zsh plugins are enabled in .zshrc. Plugins must be listed before source $ZSH/oh-my-zsh.sh.

PluginWhat it provides
aliasesals command — lists all active aliases with descriptions
brewBrew completion and bubo/bubc/bubu update aliases
colored-man-pagesANSI colour for man pages — much easier to read
dockerDocker CLI completion and aliases (dbl, dcin, dco, dps, etc.)
docker-composedocker compose completion and shorthand aliases (dco, dcup, dcdown, etc.)
ghGitHub CLI (gh) shell completion
gitExtensive Git aliases (gst, gco, glog, gcmsg, etc.) and branch in prompt
gitignoregi <lang> — fetches a .gitignore template from gitignore.io
rsyncAliases for common rsync patterns (rsync-copy, rsync-move, etc.)
sudoDouble-press ESC to prepend sudo to the current or previous command

Built-in Aliases

Key aliases defined in .zshrc (beyond plugin aliases):

AliasExpands toPurpose
bcubrew outdated --cask --greedy | xargs brew reinstallUpgrade all outdated Homebrew casks in one shot
cscheat_glowRender a cheat sheet with glow
dspdocker system prune -a -fFree all unused Docker resources
hshistory | grepCase-sensitive history search
hsihistory | grep -iCase-insensitive history search
kkubectlShort Kubernetes client
kckubectxSwitch Kubernetes context
kgkubectl getList Kubernetes resources
klkubectl logsStream pod logs
knkubensSwitch Kubernetes namespace
ladlazydockerTerminal UI for Docker
laglazygitTerminal UI for Git
pubipdig +short txt ch whoami.cloudflare @1.0.0.1Show your public IP address
armarch -arm64 /bin/zsh (macOS only)Start an ARM64 shell on Apple Silicon
intelarch -x86_64 /bin/zsh (macOS only)Start a Rosetta (x86_64) shell

fzf Integration

The fzf Oh My Zsh plugin is enabled by the Homebrew completion setup. The following key bindings are available in the terminal:

BindingAction
CTRL-TFuzzy-search files/directories and paste the path
CTRL-RFuzzy-search command history and run selected entry
ALT-CFuzzy cd into a subdirectory

cheat is also configured with CHEAT_USE_FZF=true so cheat queries are piped through fzf for interactive selection.

Local Overrides

The following files are sourced at the end of .zshrc if they exist. They are never tracked by git and always take precedence:

FilePurpose
~/.config/dotfiles/env.local.shMachine-specific secrets and environment variables
~/.zshrc.localMachine-specific aliases, path additions, and exports

Shell Functions

Custom functions live in config/shell/functions.sh and are sourced automatically by .zshrc. Run lsfn to list all functions with their help text.

FunctionUsageDescription
b64db64d <string>Decode a base64 string
b64eb64e <string>Encode a string to base64
browserbrowser [-- <url>]Start a Browsh terminal browser via Docker
cheat_glowcheat_glow <sheet>Render a cheat cheatsheet through glow at 150 columns for readability
check_certcheck_cert <url>Print TLS certificate details for a domain using curl
dksdks <secret> [namespace]Decode a Kubernetes secret — outputs all .data values base64-decoded via yq
kbpkbp <port>Kill the process currently listening on the given TCP port
randompassrandompass [length]Generate a secure random password (default 24 chars) with mixed complexity
timestampdtimestampd <unix_ts>Convert a Unix timestamp to a human-readable date (cross-platform)
timestampetimestampe <YYYY-mm-ddTHH:MM:ss>Convert a date string to its Unix timestamp (cross-platform)

Run any function with -h or --help to see its usage message, e.g. dks -h.

Git Configuration

config/git/.gitconfig is symlinked to ~/.gitconfig. Key settings:

SectionSettingValueEffect
pushdefaultsimplePush to the tracking branch only (safe default)
pullrebasetrueAlways rebase on pull instead of merge
branchautosetuprebasealwaysNew branches track their remote with rebase
rerereenabledtrueRecord and replay conflict resolutions automatically
commitgpgsigntrueSign every commit with your SSH key
taggpgSigntrueSign every tag with your SSH key
gpgformatsshUse SSH key (not GPG keyring) for signing
gpg.sshallowedSignersFile~/.ssh/allowed_signersFile that maps emails to trusted public keys
initdefaultBranchmainNew repositories default to main
credentialhelpercache --timeout=3600Cache credentials for 1 hour
difftoolmeldUse Meld as the visual diff tool

Machine-specific overrides

Create ~/.gitconfig.local with any section you want to override locally — it cannot be committed since it is excluded from the repository:

ini
# ~/.gitconfig.local — never tracked by git
[user]
  email = work@company.com
  signingkey = ~/.ssh/id_ed25519_work.pub

SSH commit signing

When ~/.ssh/id_rsa.pub exists the dotfiles install creates ~/.ssh/allowed_signers automatically. This allows Git to verify signed commits locally with git log --show-signature.

SSH Client Configuration

config/ssh/config is symlinked to ~/.ssh/config. The global Host * block applies to every connection:

DirectiveValueEffect
SendEnv -LC_* -LANG(off)Do not forward locale variables — avoids setlocale warnings on servers
ControlMasterautoReuse an existing connection if one is already open
ControlPath~/.ssh/cm-…Socket path for multiplexed connections
ControlPersist60sKeep the master connection open for 60 s after the last session closes
ServerAliveInterval60Send a keepalive packet every 60 s
ServerAliveCountMax3Drop the connection after 3 missed keepalives (~3 min)

Colima integration: If ~/.colima/ssh_config exists it is included at the top, which lets Colima VMs work seamlessly with ssh colima-* aliases.

proto Toolchain

config/proto/.prototools is symlinked to ~/.proto/.prototools. It pins global tool versions and configures proto's behaviour:

toml
bun   = "latest"
node  = "latest"
npm   = "bundled"   # ships with the Node.js version
pnpm  = "latest"

[tools.node]
bundled-npm = true  # keep npm in sync with node

[tools.npm]
shared-globals-dir = true  # global npm packages in a single shared dir

[settings]
auto-install  = true     # install missing tool versions automatically
auto-clean    = true     # remove unused tool versions after upgrades
pin-latest    = "global" # record the resolved "latest" version globally

Managing tool versions

sh
# Install / pin a specific version
proto install node 22

# Use a version in the current directory only
proto use node 20 --local

# List all tools managed by proto
proto list

# Upgrade proto itself
proto upgrade

Homebrew Tips

sh
# Update all installed packages
brew update && brew upgrade

# Remove outdated versions
brew cleanup

# List installed formulae
brew list --formula

# List installed casks
brew list --cask

# Check for issues
brew doctor

Common Issues

Xcode CLT broken after macOS update:

sh
sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install

Homebrew not found on Apple Silicon:

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

Add the above line to your ~/.zshrc if Homebrew is not found in a new shell session.