docs/bootstrap/packages/brew.md
Homebrew formulae and casks — without requiring Homebrew to be installed.
[bootstrap.packages]
"brew:postgresql@17" = "latest"
"brew:ffmpeg" = "latest"
"brew:imagemagick" = "latest"
"brew-cask:firefox" = "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 directly when the tap publishes Homebrew API
metadata (api/formula/<name>.json or api/cask/<token>.json). Use the same
fully-qualified name you would pass to Homebrew:
[bootstrap.packages]
"brew:railwaycat/emacsmacport/emacs-mac" = "latest"
"brew-cask:owner/tap/app" = "latest"
For taps whose GitHub URL cannot be inferred, add a tap source. This mirrors
[plugins]: the key is the tap name and the value is the GitHub git URL.
[bootstrap.brew.taps]
"acme/tools" = "https://github.com/acme/homebrew-tools.git"
[bootstrap.packages]
"brew:acme/tools/widget" = "latest"
"brew-cask:acme/tools/widget-app" = "latest"
mise bootstrap packages brew tap and mise bootstrap packages brew untap
manage [bootstrap.brew.taps] in mise.toml; they do not mutate a Homebrew
installation. Non-GitHub taps are not currently supported because mise needs
direct raw access to the generated API metadata.
mise bootstrap packages brew tap railwaycat/emacsmacport
mise bootstrap packages brew tap acme/tools https://github.com/acme/homebrew-tools.git
mise bootstrap packages brew untap acme/tools
Casks use the brew-cask: manager. mise fetches cask metadata directly from
the Homebrew cask API (or from tap API metadata), downloads the artifact,
verifies its sha256 when the cask provides one, extracts the archive, and
installs app bundles into /Applications while recording the version under
<prefix>/Caskroom.
[bootstrap.packages]
"brew-cask:firefox" = "latest"
"brew-cask:homebrew/cask/visual-studio-code" = "latest"
brew-cask currently supports app-bundle casks (app artifacts) from dmg,
zip, and tar archives. Casks that only install pkg installers, preflight
scripts, services, or other cask artifact types fail with a clear unsupported
artifact error instead of delegating to Homebrew.
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.
| Platform | Prefix |
|---|---|
| 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.
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.
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.
For each formula in the dependency closure (dependencies first):
@@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).codesign — required on arm64, where the kernel kills binaries whose
signature doesn't match.INSTALL_RECEIPT.json is written.<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.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:
.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.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.
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.
brew-cask supports
app bundles from common archive formats. Other artifact types fail
explicitly.brew services is not implemented.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.