As a developer who constantly switches between machines, I’ve developed a simple rule: Homebrew first. Before I even think about visiting a website to download an app, I check if it’s available through Homebrew. This single decision has revolutionized how I manage software across my development environments.
When combined with Chezmoi for dotfile management, Homebrew gets me tantalizingly close to the holy grail of development setup: complete environment synchronization. But as you’ll see, even in 2024, we’re still chasing that perfect app sync nirvana.
Remember the pre-Homebrew days? Setting up a new Mac meant:
It was tedious, error-prone, and impossible to automate effectively.
With Homebrew, software installation becomes declarative and reproducible:
# Instead of visiting websites...
brew install --cask visual-studio-code
brew install --cask docker
brew install --cask notion
brew install node
brew install git
Even better, with a Brewfile
, you can declare your entire software stack:
# Brewfile - My complete software environment
tap "homebrew/bundle"
tap "homebrew/cask"
tap "homebrew/cask-fonts"
# Essential CLI tools
brew "git"
brew "node"
brew "node"
brew "nvm"
brew install "node"
brew "yarn"
brew "watchman"
brew "go"
brew "rust"
brew "chezmoi"
# Development tools
brew "neovim"
brew "tmux"
brew "fzf"
brew "ripgrep"
brew "bat"
brew "exa"
brew "fd"
brew "jq"
brew "httpie"
# Applications
cask "visual-studio-code"
cask "docker"
cask "iterm2"
cask "notion"
cask "slack"
cask "spotify"
cask "1password"
cask "google-chrome"
cask "firefox"
# Fonts
cask "font-fira-code-nerd-font"
cask "font-jetbrains-mono-nerd-font"
# Quick Look plugins
cask "qlmarkdown"
cask "quicklook-json"
cask "quicklook-csv"
The real magic happens when you combine Homebrew with Chezmoi. My Brewfile
is managed by Chezmoi, which means:
Here’s how I set it up:
# Add Brewfile to chezmoi
chezmoi add ~/.config/homebrew/Brewfile
# Create a run_onchange script for automatic installation
# ~/.local/share/chezmoi/run_onchange_install-packages.sh.tmpl
#!/bin/bash
{{ if eq .chezmoi.os "darwin" -}}
echo "📦 Installing/updating Homebrew packages..."
brew bundle install --file ~/.config/homebrew/Brewfile --cleanup
echo "✅ Homebrew packages updated"
{{ end -}}
Now, whenever I modify my Brewfile on any machine and sync with Chezmoi, all my computers automatically install the new software!
I organize my Brewfile into logical sections:
# The foundation of my command-line workflow
brew "git" # Version control
brew "gh" # GitHub CLI
brew "neovim" # Editor
brew "tmux" # Terminal multiplexer
brew "fzf" # Fuzzy finder
brew "ripgrep" # Better grep
brew "bat" # Better cat
brew "exa" # Better ls
brew "fd" # Better find
brew "node" # JavaScript runtime
brew "python" # Python interpreter
brew "go" # Go compiler
brew "rust" # Rust toolchain
brew "deno" # Modern JS/TS runtime
cask "visual-studio-code" # Primary editor
cask "docker" # Containerization
cask "iterm2" # Better terminal
cask "notion" # Note-taking
cask "bitwarden" # Password manager
Here’s where the dream meets reality. Despite Homebrew’s extensive catalog, I still find myself manually installing several apps:
Design & Creative Tools:
Specialized Development Tools:
Enterprise Software:
For apps I can’t get through Homebrew, I’ve developed a process:
MANUAL_INSTALLS.md
in my dotfilesmas
(Mac App Store CLI)mackup
for some app settings backup# Mac App Store apps via mas
brew "mas"
# Then in Brewfile:
mas "Xcode", id: 497799835
Environment-Specific Brewfiles:
# Work machine
Brewfile.work
# Personal machine
Brewfile.personal
# Shared base
Brewfile.base
Conditional Installation:
# Install only on work machines
if ENV['USER'] == 'work_username'
cask "slack"
cask "microsoft-teams"
end
I’ve created scripts to keep everything clean and updated:
#!/bin/bash
# brew-maintenance.sh
echo "🍺 Homebrew Maintenance Script"
# Update Homebrew and all packages
brew update && brew upgrade
# Clean up old versions
brew cleanup
# Check for issues
brew doctor
# Update App Store apps
mas upgrade
# Sync with Brewfile
brew bundle check --file ~/.config/homebrew/Brewfile || \
brew bundle install --file ~/.config/homebrew/Brewfile
echo "✅ Homebrew maintenance complete"
While Homebrew gets me 80% of the way to app sync nirvana, I’m still dreaming of:
ls /Applications
and see what’s available in Homebrewbrew search <app-name>
before downloading anything# See what's installed
brew list
# See what's outdated
brew outdated
# Generate Brewfile from current installation
brew bundle dump
# Install everything from Brewfile
brew bundle install
# Remove packages not in Brewfile
brew bundle cleanup
While I haven’t achieved complete app sync nirvana, the Homebrew-first approach combined with Chezmoi has transformed my development setup process. What used to take a full day of clicking through websites and installers now takes about 30 minutes—most of which is just waiting for downloads.
The reality is that those manual installs I still need to do are mostly edge cases: enterprise software, creative tools with complex licensing, or specialized apps. For 80% of my software stack, I have achieved the dream of declarative, version-controlled, automatically synchronized installation.
And honestly? 80% perfect is pretty damn good.
The future will likely bring us even closer to that sync nirvana, but for now, I’m grateful for how far we’ve come. If you’re still manually downloading apps in 2024, give Homebrew a try. Your future self (and your next computer setup) will thank you.
Ready to start your Homebrew journey? Check out the official Homebrew documentation and see how it integrates with my Chezmoi workflow for complete environment management.