current journal · 3 min read

How to update Homebrew and all your packages

brew update vs brew upgrade, why casks get skipped and when --greedy helps, pinning, cleanup, and updating global npm packages. Commands checked on Homebrew 7.

Published November 1, 2026 · Updated November 1, 2026

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 behind

brew update vs brew upgrade

  • brew update updates Homebrew and its knowledge of the latest versions. It doesn’t change anything you installed.
  • brew upgrade installs newer versions of your outdated formulae (command-line tools) and casks (apps). It runs brew update first on its own unless you set HOMEBREW_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 node

Clean up afterwards

brew cleanup       # remove old versions and cached downloads
brew autoremove    # remove dependencies nothing needs anymore
brew doctor        # check for setup problems

brew 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 -g

npm 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 -g

Or 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.

← All articles