Homebrew: The Complete Guide for macOS Developers
macOS ships without a package manager, which makes setting up a developer machine a manual slog — download this DMG, drag that app, configure this PATH. Homebrew fixes that. It’s the de-facto standard package manager for macOS, and once you understand its model (formulae vs. casks, taps, and Brewfiles), maintaining a reproducible dev environment becomes straightforward. Let’s cover everything you actually need.
Installing Homebrew
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
The installer will prompt for your password, install Xcode Command Line Tools if needed, and set up Homebrew at /opt/homebrew on Apple Silicon or /usr/local on Intel Macs.
After installation, follow the on-screen instructions to add Homebrew to your PATH. For Apple Silicon:
$ echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
$ eval "$(/opt/homebrew/bin/brew shellenv)"
Verify it works:
$ brew --version
Homebrew 4.3.1
Formulae vs. Casks
Homebrew has two types of packages:
- Formulae — command-line tools and libraries (
git,node,postgresql,ffmpeg). Installed to/opt/homebrew/bin. - Casks — macOS GUI applications (
--caskflag). Installed to/Applicationslike a normal app.
$ brew install git # formula — installs the git CLI
$ brew install --cask iterm2 # cask — installs the iTerm2.app GUI
Daily Commands
$ brew search <term> # search available packages
$ brew install <name> # install a package
$ brew uninstall <name> # remove a package
$ brew upgrade # upgrade all outdated packages
$ brew upgrade <name> # upgrade a specific package
$ brew list # list installed formulae
$ brew list --cask # list installed casks
$ brew info <name> # show package details and dependencies
$ brew doctor # diagnose common Homebrew issues
$ brew cleanup # remove old versions to free disk space
Seeing What’s Outdated
$ brew outdated
git (2.43.0) < 2.46.0
node (20.11.0) < 22.4.0
postgresql@16 (16.2) < 16.4
$ brew upgrade
==> Upgrading 3 outdated packages:
git 2.43.0 -> 2.46.0
node 20.11.0 -> 22.4.0
postgresql@16 16.2 -> 16.4
Installing Multiple Versions with @
Some formulae support pinned versions via the @ convention:
$ brew install node@20 # install Node.js 20 LTS
$ brew install python@3.11 # install Python 3.11 specifically
$ brew install postgresql@16 # PostgreSQL 16
To switch between versions, unlink one and link another:
$ brew unlink node@22
$ brew link node@20 --force
Taps — Third-Party Repositories
Homebrew’s built-in repository (called homebrew/core) covers thousands of packages, but some tools are distributed via their own taps:
$ brew tap hashicorp/tap
$ brew install hashicorp/tap/terraform
$ brew tap mongodb/brew
$ brew install mongodb/brew/mongodb-community
$ brew tap homebrew/cask-fonts
$ brew install --cask font-jetbrains-mono
A tap is just a GitHub repository with a specific file layout. brew tap <user>/<repo> clones it to $(brew --repository)/Library/Taps/.
Brewfile — Reproducible Environments
The Brewfile is Homebrew’s equivalent of a package.json — a declarative file listing all your packages. This is how you set up a new machine in minutes instead of hours.
Create one interactively from your current installation:
$ brew bundle dump --file=~/Brewfile
A typical Brewfile looks like:
tap "homebrew/bundle"
tap "homebrew/cask-fonts"
tap "hashicorp/tap"
brew "git"
brew "node"
brew "python@3.12"
brew "postgresql@16"
brew "redis"
brew "ffmpeg"
brew "jq"
brew "ripgrep"
brew "tmux"
brew "gh"
cask "iterm2"
cask "visual-studio-code"
cask "docker"
cask "rectangle"
cask "font-jetbrains-mono"
To restore from a Brewfile on a new machine:
$ brew bundle install --file=~/Brewfile
Installing git... done
Installing node... done
Installing python@3.12... done
...
Homebrew Bundle complete! 14 Brewfile dependencies now installed.
Keep your Brewfile in a dotfiles git repository. It’s the fastest way to get a new Mac developer-ready.
Services — Running Background Processes
Many developer tools (PostgreSQL, Redis, nginx) need to run as background services. Homebrew wraps macOS’s launchctl so you don’t have to:
$ brew services start postgresql@16
==> Successfully started postgresql@16 (label: homebrew.mxcl.postgresql@16)
$ brew services stop redis
$ brew services restart nginx
$ brew services list
Name Status User File
nginx started mukulkadel ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist
postgresql@16 started mukulkadel ~/Library/LaunchAgents/homebrew.mxcl.postgresql@16.plist
redis none
Services started this way survive reboots automatically.
Troubleshooting Common Issues
brew doctor Warnings
Run brew doctor when things feel off. Most warnings come with instructions:
$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. Most probably won't cause any issues.
Warning: Unbrewed dylibs were found in /usr/local/lib
Package Conflicts
If a package conflicts with a system tool or another Homebrew package:
$ brew link --overwrite <name> # force linking
$ brew unlink <name> # unlink to resolve a conflict
Stale Lock Files
If a brew upgrade dies mid-run and leaves Homebrew locked:
$ rm -rf $(brew --prefix)/var/homebrew/locks/*
Conclusion
Homebrew is the starting point for any macOS developer environment. The core workflow — brew install, brew upgrade, brew services — covers 90% of daily use. The Brewfile is where the real leverage is: commit it to your dotfiles repo and you can rebuild a complete dev environment on a new machine with a single command. Run brew doctor periodically to catch drift before it becomes a problem.