Appendix B — Brewfile
brew bundle reads a Ruby-flavoured manifest and installs everything in it — formulae, casks, Mac App Store apps (via mas), and VS Code extensions. It is the single most useful file in a dotfiles repo: the whole software side of a Mac in 300 lines, reproducible with one command. Chapter 8 explains Homebrew itself; this appendix is the manifest.
brew bundle --file=scripts/Brewfile # install what's listed (skips what's already there)
brew bundle check --file=scripts/Brewfile # what's missing?
brew bundle cleanup --file=scripts/Brewfile # what's installed but NOT listed? (add --force to uninstall)
brew bundle dump --force --describe # regenerate from what's installed → ./BrewfileConventions in this file#
- Lines starting with
#cask,#brew,#mas,#vscode(no space) are opt-in alternatives — remove the#to enable. Lines starting with#are commentary. - The uncommented set is the guide's default recommendation: a full developer CLI,
mise+uv, container/cloud tooling, Nerd Fonts, Ghostty, VS Code, OrbStack, Firefox, and the Tier-1 daily-driver apps from chapter 19. On a fast connection it's ~15–25 minutes and ~8 GB (Xcode excluded). restart_service: falseonpostgresql@18andredismeans they're installed but not started at login —brew services run postgresql@18when you need them (chapter 15).masentries need you to be signed in to the App Store; the bootstrap script drops them otherwise. Find IDs withmas search "name".vscodeentries runcode --install-extension; they're skipped if VS Code isn't installed.- Third-party taps (
supabase/tap,nikitabobko/tap) will prompt for trust on first use under Homebrew 6 — that's the new tap-trust feature working as intended.
scripts/Brewfile#
# Brewfile — The macOS Setup Guide (Appendix B). September 2026.
# Install everything: brew bundle --file=scripts/Brewfile
# Only the essentials: comment out the "Optional" sections, or copy the lines you want into ~/.Brewfile.
# Keep it current: brew bundle dump --force --describe --file=~/.Brewfile
# Lines starting with "# " are commentary; lines starting with "#brew"/"#cask" are opt-in — delete the "#" to enable.
tap "homebrew/services" # brew services (launchd wrappers)
# tap "oven-sh/bun" # example third-party tap — Homebrew 6 will prompt to trust it
# ─── Core CLI ─────────────────────────────────────────────────────────────────
brew "git" # newer than Apple's
brew "gh" # GitHub CLI
brew "git-delta" # better diffs
brew "git-lfs"
brew "lazygit" # TUI for git
brew "coreutils" # GNU utils as g-prefixed (gls, gdate…)
brew "gnu-sed"
brew "findutils"
brew "grep"
brew "bash" # bash 5 (macOS ships 3.2)
brew "zsh-autosuggestions"
brew "zsh-syntax-highlighting"
brew "zsh-completions"
brew "starship" # prompt
brew "fzf" # fuzzy finder (ctrl-r, ctrl-t)
brew "zoxide" # smarter cd
brew "eza" # ls replacement
brew "bat" # cat with syntax highlighting
brew "fd" # find replacement
brew "ripgrep" # grep replacement
brew "sd" # sed replacement for simple cases
brew "jq" # JSON
brew "yq" # YAML
brew "fx" # interactive JSON viewer
brew "tree"
brew "dust" # du replacement
brew "duf" # df replacement
brew "ncdu"
brew "procs" # ps replacement
brew "bottom" # top replacement (btm)
brew "htop"
brew "hyperfine" # benchmarking
brew "tldr" # simplified man pages (tlrc)
brew "tmux"
brew "zellij" # tmux alternative
brew "neovim"
brew "watch"
brew "wget"
brew "curl"
brew "httpie"
brew "xh" # httpie in Rust
brew "openssh"
brew "mosh"
brew "rsync"
brew "rclone"
brew "p7zip"
brew "unar"
brew "trash" # rm → Trash
brew "mas" # Mac App Store CLI
brew "duti" # set default apps
brew "terminal-notifier"
brew "ical-buddy"
brew "switchaudio-osx" # switch audio devices from CLI
brew "pandoc"
brew "shellcheck"
brew "shfmt"
brew "direnv"
brew "age" # modern file encryption
brew "sops"
brew "gnupg"
brew "pinentry-mac"
brew "ykman" # YubiKey manager
brew "nmap"
brew "mtr"
brew "iperf3"
brew "dog" # dig replacement (or: brew "doggo")
brew "wireguard-tools"
brew "ffmpeg"
brew "imagemagick"
brew "exiftool"
brew "pngquant"
brew "oxipng"
brew "graphviz"
brew "poppler" # pdftotext etc.
brew "qpdf"
brew "sqlite"
brew "whisper-cpp"
# ─── Runtimes & language tooling ──────────────────────────────────────────────
brew "mise" # runtime versions (node, python, java, go, rust, ruby…)
brew "uv" # Python packaging
brew "ruff"
brew "pipx" # optional if you use uv tool
brew "pnpm" # or via corepack/mise
brew "cmake"
brew "ninja"
brew "llvm" # newer clang/clangd/lldb than CLT (optional; big)
brew "gcc" # real GCC for coursework (gcc-15)
brew "make" # GNU make 4 as gmake
brew "bear" # compile_commands.json for clangd
brew "ccache"
brew "typst"
brew "tectonic" # self-contained LaTeX (or cask mactex-no-gui)
brew "protobuf"
brew "grpcurl"
brew "sqlfluff"
brew "postgresql@18", restart_service: false
brew "redis", restart_service: false
brew "pgcli"
brew "mycli"
brew "usql" # universal SQL CLI
brew "duckdb"
# ─── Containers, cloud, DevOps ────────────────────────────────────────────────
brew "docker" # CLI only; runtime = OrbStack (cask below) or Docker Desktop
brew "docker-compose"
brew "docker-buildx"
brew "dive" # explore image layers
brew "lazydocker"
brew "kubectl"
brew "kubectx"
brew "k9s"
brew "helm"
brew "kind"
brew "stern"
brew "awscli"
brew "aws-vault"
brew "azure-cli"
brew "opentofu" # or "terraform"
brew "ansible"
brew "cloudflared"
brew "wrangler"
brew "flyctl"
brew "vercel-cli"
brew "supabase/tap/supabase"
brew "ngrok"
brew "tailscale" # CLI (cask "tailscale-app" for the menu bar app)
brew "act" # run GitHub Actions locally
brew "actionlint"
brew "pre-commit"
brew "lefthook"
brew "just" # command runner
brew "watchman"
brew "mkcert" # local HTTPS certs
brew "caddy"
brew "step"
brew "trivy" # vuln scanner
brew "gitleaks"
brew "cosign"
# ─── Fonts ────────────────────────────────────────────────────────────────────
cask "font-jetbrains-mono-nerd-font"
cask "font-fira-code-nerd-font"
cask "font-meslo-lg-nerd-font"
cask "font-inter"
cask "font-ibm-plex"
cask "font-monaspace"
#cask "font-cascadia-code-nf"
#cask "font-iosevka-nerd-font"
# ─── Terminal, editors, dev apps ──────────────────────────────────────────────
cask "ghostty"
#cask "iterm2"
#cask "wezterm"
#cask "warp"
cask "visual-studio-code"
#cask "cursor"
#cask "zed"
#cask "jetbrains-toolbox"
#cask "sublime-text"
cask "orbstack" # containers + Linux VMs; replaces Docker Desktop
#cask "docker-desktop"
#cask "podman-desktop"
cask "utm" # VMs (QEMU); free
#cask "vmware-fusion"
#cask "parallels"
cask "tableplus"
#cask "postico"
#cask "dbeaver-community"
#cask "pgadmin4"
#cask "mongodb-compass"
cask "bruno" # API client (Postman alternative)
#cask "postman"
#cask "insomnia"
#cask "proxyman"
#cask "charles"
cask "fork" # Git GUI (free tier)
#cask "sublime-merge"
#cask "tower"
#cask "github"
#cask "kaleidoscope"
cask "dash" # offline docs (paid)
#cask "devdocs"
#cask "xcodes" # manage Xcode versions
#cask "android-studio"
#cask "figma"
#cask "sf-symbols"
#cask "lm-studio"
#cask "ollama-app"
#cask "wireshark-app"
#cask "gpg-suite"
#cask "yubico-authenticator"
#cask "launchcontrol"
#cask "suspicious-package"
#cask "macfuse"
# ─── Browsers ─────────────────────────────────────────────────────────────────
cask "firefox"
#cask "google-chrome"
#cask "brave-browser"
#cask "zen"
#cask "vivaldi"
#cask "orion"
#cask "chromium"
# ─── Daily-driver essentials (Tier 1, all free) ──────────────────────────────
cask "raycast" # launcher; or "alfred"
cask "rectangle" # window snapping; or "aerospace" below
#cask "nikitabobko/tap/aerospace"
cask "maccy" # clipboard history (skip if using Raycast's)
cask "jordanbaird-ice" # menu bar icon manager
cask "stats" # system stats in menu bar
cask "itsycal" # menu bar calendar
cask "iina" # video player
cask "shottr" # screenshots + OCR
cask "appcleaner" # or "pearcleaner"
cask "the-unarchiver"
cask "karabiner-elements" # key remapping
cask "hammerspoon" # Lua automation
cask "bitwarden" # or "1password" + "1password-cli"
cask "obsidian"
cask "zotero"
cask "keka"
cask "monitorcontrol"
cask "linearmouse"
cask "latest" # app update checker
cask "qlmarkdown" # Quick Look plugins ↓
cask "syntax-highlight"
cask "quicklook-json"
cask "qlvideo"
# ─── Optional daily-driver apps ───────────────────────────────────────────────
#cask "1password"
#cask "1password-cli"
#cask "keepassxc"
#cask "alfred"
#cask "aldente"
#cask "betterdisplay"
#cask "lulu"
#cask "notion"
#cask "logseq"
#cask "anytype"
#cask "typora"
#cask "skim"
#cask "sioyek"
#cask "anki"
#cask "netnewswire"
#cask "syncthing-app"
#cask "maestral"
#cask "google-drive"
#cask "dropbox"
#cask "onedrive"
#cask "tailscale-app"
#cask "cyberduck"
#cask "transmit"
#cask "handbrake-app"
#cask "kap"
#cask "obs"
#cask "audacity"
#cask "imageoptim"
#cask "pixelmator-pro" # via App Store (mas) instead
#cask "affinity"
#cask "drawio"
#cask "excalidraw"
#cask "libreoffice"
#cask "microsoft-office"
#cask "mactex-no-gui"
#cask "texshop"
#cask "miniforge"
#cask "r"
#cask "rstudio"
#cask "positron"
#cask "racket"
#cask "logisim-evolution"
#cask "mathpix-snipping-tool"
#cask "steam"
#cask "whisky"
#cask "crossover"
#cask "espanso"
#cask "keyboard-maestro"
#cask "bettertouchtool"
#cask "hazel"
#cask "numi"
#cask "rocket"
#cask "keyboardcleantool"
#cask "coconutbattery"
#cask "grandperspective"
#cask "daisydisk"
#cask "omnidisksweeper"
#cask "macs-fan-control"
#cask "istat-menus"
#cask "cleanshot"
#cask "screen-studio"
#cask "fantastical"
#cask "busycal"
#cask "mimestream"
#cask "thunderbird"
#cask "spotify"
#cask "slack"
#cask "discord"
#cask "zoom"
#cask "microsoft-teams"
#cask "signal"
#cask "telegram"
#cask "whatsapp"
#cask "backblaze"
#cask "arq"
#cask "carbon-copy-cloner"
#cask "little-snitch"
#cask "malwarebytes"
# ─── Mac App Store (needs: mas + signed into the App Store) ──────────────────
# Find IDs with: mas search "name"
mas "Xcode", id: 497799835 # 12+ GB; only if doing Apple-platform dev
#mas "Amphetamine", id: 937984704
#mas "Things 3", id: 904280696
#mas "Pixelmator Pro", id: 1289583905
#mas "Bear", id: 1091189122
#mas "Wipr 2", id: 1662217862 # Safari content blocker
#mas "AdGuard for Safari", id: 1440147259
#mas "Refined GitHub", id: 1519867270 # Safari extension
#mas "Keynote", id: 409183694
#mas "Pages", id: 409201541
#mas "Numbers", id: 409203825
#mas "Apple Configurator", id: 1037126344 # DFU revive
#mas "TestFlight", id: 899247664
#mas "Kindle", id: 302584613
#mas "WhatsApp Messenger", id: 310633997
#mas "Slack for Desktop", id: 803453959
# ─── VS Code extensions (brew bundle installs these via `code --install-extension`) ──
vscode "editorconfig.editorconfig"
vscode "esbenp.prettier-vscode"
vscode "biomejs.biome"
vscode "dbaeumer.vscode-eslint"
vscode "charliermarsh.ruff"
vscode "ms-python.python"
vscode "ms-python.vscode-pylance"
vscode "ms-toolsai.jupyter"
vscode "golang.go"
vscode "rust-lang.rust-analyzer"
vscode "vadimcn.vscode-lldb"
vscode "llvm-vs-code-extensions.vscode-clangd"
vscode "redhat.java"
vscode "vscjava.vscode-java-pack"
vscode "ms-azuretools.vscode-containers"
vscode "ms-vscode-remote.remote-ssh"
vscode "ms-vscode-remote.remote-containers"
vscode "github.copilot"
vscode "github.copilot-chat"
vscode "github.vscode-pull-request-github"
vscode "github.vscode-github-actions"
vscode "eamodio.gitlens"
vscode "mhutchie.git-graph"
vscode "usernamehw.errorlens"
vscode "streetsidesoftware.code-spell-checker"
vscode "yzhang.markdown-all-in-one"
vscode "bierner.markdown-mermaid"
vscode "james-yu.latex-workshop"
vscode "myriad-dreamin.tinymist"
vscode "tamasfe.even-better-toml"
vscode "redhat.vscode-yaml"
vscode "timonwong.shellcheck"
vscode "foxundermoon.shell-format"
vscode "mkhl.direnv"
vscode "hashicorp.terraform"
vscode "ms-kubernetes-tools.vscode-kubernetes-tools"
vscode "pkief.material-icon-theme"
vscode "github.github-vscode-theme"
#vscode "vscodevim.vim"
#vscode "asvetliakov.vscode-neovim"
#vscode "anthropic.claude-code"
#vscode "continue.continue"Per-machine Brewfiles#
One file rarely fits a laptop, a desktop, and a work machine. Two patterns:
Includes — a base file plus a per-host file, concatenated at install time:
# ~/.dotfiles/install.sh
cat Brewfile.base "Brewfile.$(scutil --get ComputerName | tr ' ' '-')" 2>/dev/null > /tmp/Brewfile
brew bundle --file=/tmp/BrewfileRuby conditionals — the Brewfile is Ruby, so this works:
host = `scutil --get ComputerName`.strip
cask "docker-desktop" if host == "work-mbp" # company mandates Docker Desktop
cask "orbstack" unless host == "work-mbp"
cask "steam" if host == "studio"
brew "postgresql@18", restart_service: (host == "studio")Keeping it honest#
Every month (the routine in chapter 18):
brew bundle cleanup --file=~/.dotfiles/Brewfile # lists things you installed ad hoc
# → either add them to the Brewfile (you use them) or `brew uninstall --zap` them (you don't)
brew bundle dump --force --describe --file=/tmp/Brewfile.now && diff ~/.dotfiles/Brewfile /tmp/Brewfile.nowbrew bundle dump sorts alphabetically and loses your comments, so don't overwrite a hand-curated file with it — diff and copy the new lines across. --describe adds each formula's description as a comment, which makes a dumped file readable enough to start from.
Formula and cask name changes#
Homebrew renames casks when upstream renames apps (docker → docker-desktop, tailscale → tailscale-app, handbrake → handbrake-app, wireshark → wireshark-app all happened in 2025–26). brew bundle prints a deprecation warning with the new name; update the file. brew search --cask name finds the current one.