Skip to content

brew experimental

Homebrew formulae from homebrew/corewithout requiring Homebrew to be installed.

toml
[bootstrap.packages]
"brew:postgresql@17" = "latest"
"brew:ffmpeg" = "latest"
"brew:imagemagick" = "latest"

mise installs homebrew/core formulae directly into the canonical Homebrew prefix — /opt/homebrew on arm64 macOS, /home/linuxbrew/.linuxbrew on Linux. It fetches metadata from the formulae.brew.sh API, resolves the runtime dependency closure, downloads prebuilt bottles from ghcr.io (verifying sha256 checksums), and performs the same relocation, code-signing, and linking work brew does when pouring a bottle. Formulae without a usable bottle are built from source, also without Homebrew (see Source formulae). mise never shells out to brew for homebrew/core formulae.

Third-party taps are supported when Homebrew itself is installed. Tapped formulae are delegated to a real brew command; use the same fully-qualified formula name you would pass to brew install:

toml
[bootstrap.packages]
"brew:railwaycat/emacsmacport/emacs-mac" = "latest"

For non-GitHub taps, or taps whose URL cannot be inferred by Homebrew, add a tap source. This mirrors [plugins]: the key is the tap name and the value is the git URL.

toml
[bootstrap.brew.taps]
"acme/tools" = "https://git.example.com/acme/homebrew-tools.git"

[bootstrap.packages]
"brew:acme/tools/widget" = "latest"

Before installing or upgrading tapped formulae, mise runs brew tap for any configured tap URL and then brew update-if-needed so the tap is current.

You can also manage taps imperatively, matching mise plugins install / mise plugins uninstall: these commands shell out to Homebrew and do not modify mise.toml.

sh
mise bootstrap packages brew tap railwaycat/emacsmacport
mise bootstrap packages brew tap acme/tools https://git.example.com/acme/homebrew-tools.git
mise bootstrap packages brew untap acme/tools

This exists because shared-library packages — postgres, ffmpeg, imagemagick, php — fundamentally can't be served by mise's per-project backends like aqua: or github:: their bottles are built against fixed install paths and a shared dependency tree. Installing them at Homebrew's canonical prefix is what makes them work.

Supported platforms

PlatformPrefix
macOS arm64 (Apple Silicon)/opt/homebrew
Linux x86_64/home/linuxbrew/.linuxbrew
Linux arm64/home/linuxbrew/.linuxbrew

Intel macs are not supported — the brew manager reports itself unavailable there. On Linux, formulae without a bottle for your architecture (arm64 Linux bottles exist for most but not all of homebrew/core) are built from source instead.

The prefix

If the prefix doesn't exist, mise creates it with the standard layout — the only time the brew manager uses sudo, mirroring what Homebrew's own installer does (mkdir + chown to your user). After that, installs are plain file operations as your user; nothing runs as root.

Coexistence with a real Homebrew

mise pours bottles into the Cellar exactly the way brew does and writes brew-compatible INSTALL_RECEIPT.json files into every keg. To a real Homebrew installation, mise-poured kegs look like its own: brew list, brew upgrade, and brew uninstall all work on them. Conversely, mise's status checks read the Cellar directly, so formulae installed by brew count as installed.

mise tracks what it installed in its own ledger (~/.local/state/mise/system/brew.json) and never overwrites files in the prefix that it (or brew) didn't create — link conflicts fail with a list of the offending files rather than clobbering them.

How pouring works

For each formula in the dependency closure (dependencies first):

  1. Fetch the bottle for your platform from ghcr.io and verify its sha256 against the API metadata.
  2. Extract into a temporary directory inside the Cellar (incomplete pours are never visible as installed packages).
  3. Relocate: bottles embed placeholder paths like @@HOMEBREW_PREFIX@@. mise rewrites them to real paths — plain replacement in text files, in-place and load-command rewriting in Mach-O binaries (growing load commands into header padding when needed, exactly like brew's ruby-macho does). On Linux, the ELF interpreter and rpath are patched the way brew's PatchELF gem does it: strings that no longer fit are moved into a new segment appended to the binary, and the interpreter is pointed at <prefix>/lib/ld.so (a symlink mise maintains to the system's dynamic loader, or to a brewed glibc when one is installed).
  4. Re-sign (macOS): any modified binary is ad-hoc re-signed with codesign — required on arm64, where the kernel kills binaries whose signature doesn't match.
  5. Receipt: a brew-compatible INSTALL_RECEIPT.json is written.
  6. Link: <prefix>/opt/<name> is created and the keg's bin, lib, include, share, etc. are symlinked into the prefix — keg-only formulae get the opt link but are not linked into the prefix, same as brew.

Source formulae

A few formulae have no bottle at all (source-only formulae), and some have bottles for other platforms but not yours. mise builds those from source — still without Homebrew:

  1. Ruby — a formula is Ruby code, so mise provisions a mise-managed ruby through its normal tool machinery (precompiled, fast; respects your configured ruby if you have one).
  2. Formula — the formula's .rb is downloaded from homebrew/core, pinned to the exact commit the API metadata was generated from and verified against the API's sha256 for it.
  3. Source — the stable source archive is downloaded and verified against the API's sha256.
  4. Build deps — the formula's build dependencies (cmake, pkgconf, ...) are added to the install closure and poured as regular bottles first.
  5. Build — mise evaluates the formula with its own Formula-DSL shim and runs def install against the canonical prefix, with PATH, PKG_CONFIG_PATH, and compiler flags pointing at the dependency kegs. The keg gets the same brew-compatible receipt as a poured bottle, with poured_from_bottle: false — exactly how brew marks its own source builds.

The shim implements the commonly-used subset of the formula DSL (configure/cmake/meson-style builds, resources, patches, the standard path and environment helpers). Formulae that use parts of the DSL the shim doesn't cover — language-specific helpers like virtualenv_install_with_resources, VCS downloads, and similar — fail with a clear formula uses ... error rather than miscompiling silently.

Source builds need a working toolchain (Xcode Command Line Tools on macOS, gcc/make on Linux), exactly as they would under plain Homebrew.

Upgrades

mise bootstrap packages upgrade re-resolves the configured formulae against the formulae.brew.sh API and pours any whose current version differs from the linked keg — the new keg replaces the old one and the links are repointed, the same dance brew upgrade does. Since bottles only exist for a formula's current version, "upgrade" and "install the current bottle" are the same operation.

Limitations

  • Formulae only. Casks (GUI apps) and brew services are not implemented.
  • Tapped formulae require Homebrew. mise's direct bottle/source installer is only for homebrew/core. Fully-qualified third-party tap formulae are delegated to a real brew command.
  • Source builds cover the common formula shapes. mise's formula shim implements the widely-used subset of the DSL (see Source formulae); formulae that reach beyond it fail with a clear error naming the unsupported feature.
  • Use canonical formula names. postgresql@17 is a formula name, not a mise version pin — the API's current stable version decides what gets installed. Aliases (postgres) install correctly but mise bootstrap packages status can't track them; mise warns and tells you the canonical name.
  • PATH is up to you: <prefix>/bin must be on PATH to use linked binaries, just like with Homebrew itself.
MIT LicenseCopyright © 2026en.dev