Mac Homebrew Essentials: brew install a Specific Version

Updated

Homebrew package manager on macOS

If Homebrew still carries the version you want as a versioned formula, one command does it: brew install postgresql@16. If not, because you need an exact patch release or one Homebrew has dropped, brew version-install pulls the old formula from Homebrew’s Git history into a tap of your own and installs it. These steps are for Homebrew 7 (released September 2026); brew --version shows yours.

Install a specific version with brew install name@version

Versioned formulae are named name@version. Search for them with a regular expression anchored to the start:

$ brew search /^postgresql@/
==> Formulae
postgresql@12 (disabled)
postgresql@13 (disabled)
postgresql@14 (deprecated)
postgresql@15
postgresql@16
postgresql@17
postgresql@18

A (deprecated) formula still installs, with a warning. A (disabled) one will not, and Homebrew deletes disabled formulae a year after disabling them. brew info postgresql@16 shows the exact version and tags the formula [keg-only]. Install it by name:

brew install postgresql@16

Homebrew 7 prints the plan first and asks you to press y if it has to install more than the formula you named; -y skips the question. Homebrew generally keeps at most five versions of a formula, and each tracks a major or minor release, so brew upgrade still moves it to newer patch releases. For an exact patch release, use a later section or a version manager.

Switch to the version you installed (brew link and PATH)

Most versioned formulae are keg-only: Homebrew keeps them in its Cellar instead of linking them into /opt/homebrew/bin. The main exception is Python: python@3.12 is not keg-only and adds a python3.12 command. Since Homebrew 5.1, though, a versioned formula you install directly is normally linked anyway, as long as no other version of that software is installed. If one is, Homebrew warns that the new one “was installed but not linked because” the other version is already linked, and the command keeps running the old version. Link the new one yourself:

brew link postgresql@16

Homebrew first unlinks whichever related version is linked, so this one command also switches between installed versions, and brew link postgresql@17 switches back. brew unlink postgresql@16 removes its links without uninstalling anything. A versioned formula does not need --force. If Homebrew says a formula “is keg-only and must be linked with `–force`”, it is keg-only for another reason.

I would link it. To leave the links alone instead, call the version by its full path, /opt/homebrew/opt/postgresql@16/bin/psql, or put its bin directory first on your PATH:

echo 'export PATH=/opt/homebrew/opt/postgresql@16/bin:$PATH' >> ~/.zshrc

Open a new Terminal window afterwards. On an Intel Mac the prefix is /usr/local. If psql --version still shows the wrong version, another copy is earlier on your PATH, and Homebrew 7 lists such shadowed commands in the install caveats.

Install a version Homebrew no longer carries (brew version-install)

For an exact patch release or a version Homebrew has dropped, use brew version-install (Homebrew 5.1 or later). It relies on brew extract, which searches Git history that API-based installs do not keep, so clone homebrew/core first:

brew tap --force homebrew/core
brew version-install foo 1.2.3

Replace foo and 1.2.3 with your formula and the version you want. Without the clone you get “This command requires the tap homebrew/core”, and plain brew tap homebrew/core is refused (“no longer typically necessary”) unless you add --force. The repository is about 1.2 GB on GitHub, and brew untap homebrew/core afterwards gets the space back.

brew version-install creates a personal tap (yourname/versions), extracts that version into it and installs it. It can only recover a version the formula really shipped at some point; anything else ends with “Could not find foo! The formula or version may not have existed.”

Homebrew warns that you now own the formula, which “will not receive any bugfix/security updates”. It has no bottle, so it builds from source and needs the Xcode Command Line Tools, and it may use syntax Homebrew has since removed (see Homebrew’s Versions page). If the current version of foo is linked, the build can succeed and the link step still fail with “is a symlink belonging to foo”. Run brew unlink foo, then brew link foo@1.2.3.

The manual route: brew tap-new and brew extract

This is what brew version-install automates, and it leaves you the formula file. Make a tap, extract into it, then install by full name:

brew tap-new --no-git yourname/versions
brew extract --version=1.2.3 foo yourname/versions
brew install yourname/versions/foo@1.2.3

You still need the homebrew/core clone. Extract refuses to write into homebrew/core, which is why the tap is yours, and --no-git skips the Git repository. Install by full name because, since Homebrew 6, that trusts just that formula, while a short name needs brew trust --formula first (see Tap Trust).

brew tap-new and brew extract are developer commands. The first time you run one, Homebrew warns you and turns on developer mode, so brew update follows the main branch until you run brew developer off.

Stop brew upgrade from changing a version (brew pin)

brew pin postgresql@16 stops brew upgrade from upgrading it, and brew unpin postgresql@16 reverses that. A pin freezes what is installed now, so install the version you want first. A pinned package gets no security updates, and brew pin --help warns that “Other packages which depend on newer versions of a pinned formula might not install or run correctly.”

What no longer works: brew switch, raw URLs and file paths

Old advice What you get now Since Use instead
brew switch node 22 Unknown command: brew switch Removed in 3.0.0 (February 2021) brew link node@22
brew install https://raw.githubusercontent.com/.../foo.rb No available formula or cask with the name “https://…” (older versions: “Non-checksummed download of foo formula file from an arbitrary URL is unsupported!”) Disabled in 2.5.0 (September 2020) brew version-install foo 1.2
brew install ./foo.rb Homebrew requires formulae to be in a tap, rejecting: 4.6.4 (August 2025), unless HOMEBREW_DEVELOPER is set Put it in a tap, install by full name

When mise, asdf, nvm or pyenv is the better tool

I would use a version manager when the version belongs to a project rather than to your Mac, or when you need an exact release. These tools install exact releases and record the choice per project.

  • mise and asdf handle many languages and tools. mise use node@22 installs Node 22 and writes it to mise.toml. asdf set nodejs 22.11.0 writes .tool-versions.
  • nvm is Node only: nvm install 22.11.0, with a .nvmrc file in the project. Its README says installing nvm through Homebrew is not supported, so use its own install script.
  • pyenv is Python only: brew install pyenv, then pyenv install 3.12.7 and pyenv local 3.12.7, which writes .python-version. It builds Python from source, so install its build dependencies first.
Photo of author
Sudhir P. founded HeatWare.com in 1999 and has built and operated it full-stack ever since; it is now used by more than 88,000 people. He writes here about the PostgreSQL, MySQL, Linux and DevOps work that keeps it running. Articles are rewritten only after the commands in them have actually been run, and the verification scripts are published alongside them so anyone can check the claims. Reach him at blog@heatware.net.