Homebrew keeps two things up to date: Homebrew itself, with its list of available packages, and the packages you installed. Two different commands do those two jobs, and most confusion about “brew didn’t update my app” comes from mixing them up. Every command below was checked against Homebrew 7.0.
The short version
brew update # refresh Homebrew and its package lists
brew outdated # see what's behind
brew upgrade # upgrade everything that's behindbrew update vs brew upgrade
brew updateupdates Homebrew and its knowledge of the latest versions. It doesn’t change anything you installed.brew upgradeinstalls newer versions of your outdated formulae (command-line tools) and casks (apps). It runsbrew updatefirst on its own unless you setHOMEBREW_NO_AUTO_UPDATE.
To upgrade a single package, name it: brew upgrade wget or brew upgrade --cask firefox. To see what would happen without changing anything, add --dry-run.
Why some apps never show as outdated
Many casks are apps that update themselves (they’re marked auto_updates true), and some always point to the developer’s newest download (version :latest). Homebrew skips both by default, so it doesn’t fight the app’s own updater. That’s why brew outdated can look clean while an app is behind. To include them:
brew outdated --cask --greedy
brew upgrade --cask --greedy--greedy-auto-updates includes only the self-updating apps, and --greedy-latest only the :latest ones. Two things to know before going greedy:
- An app that already updated itself can be reinstalled by Homebrew, because Homebrew still has the older version on record.
- macOS may ask whether Terminal can modify apps (App Management). Allow it in System Settings → Privacy & Security → App Management, or the upgrade fails.
You can make greedy the default by setting HOMEBREW_UPGRADE_GREEDY=1 in your shell profile. The Homebrew manual lists every option.
Hold a package back with pin
If a project depends on a specific version, pin it so brew upgrade leaves it alone:
brew pin node
brew list --pinned
brew unpin nodeClean up afterwards
brew cleanup # remove old versions and cached downloads
brew autoremove # remove dependencies nothing needs anymore
brew doctor # check for setup problemsbrew upgrade already runs brew cleanup for the packages it upgraded, unless you set HOMEBREW_NO_INSTALL_CLEANUP. Running it yourself now and then frees space from older installs.
Don’t forget global npm packages
Tools you installed with npm install -g live outside Homebrew, even if Homebrew installed Node itself:
npm outdated -g
npm update -gnpm treats global packages as if they were installed with a caret range, so npm update -g moves each one to the newest version within its current major version. For a new major version, install it explicitly: npm install -g name@latest. The npm update docs cover the details.
A weekly routine
brew update && brew upgrade && brew cleanup
npm update -gOr see it all in one list
If you also have apps from the App Store and developers’ websites, the command line covers only part of your Mac. Current, which we make, lists outdated Homebrew formulae and global npm packages next to your apps and updates them from the same window, using Homebrew and npm themselves. For everything else on your Mac, see How to update all apps on your Mac.