This guide is for the people who operate Homebrew at depth: administrators rolling it out across machines, power users who want to understand what happens under the hood, and developers who write or maintain formulae and casks. It documents Homebrew's architecture, its complete configuration surface, its security model, day-to-day operations, internals, and the developer command set.
For end-user installation and everyday package management, see the companion User Guide. This guide assumes you've read it.
Table of Contents
- Executive Summary
- Architecture
- Filesystem Layout
- Requirements & Platform Support
- Installation & Deployment
- Configuration Reference (Environment Variables)
- Security Model
- Operations
- The Formula & Cask Model
- Developer Commands
- Codebase Internals
- Testing & Quality
- Troubleshooting (Operational)
- Potential Improvements
- Quick Reference
1. Executive Summary
Homebrew is a package manager for macOS and Linux written in Ruby. It installs software into a self-contained prefix owned by the installing user, then symlinks the results onto the user's PATH. It deliberately avoids sudo, system directories, and OS package managers, which makes installs and removals clean and reversible.
There are two package types. Formulae are built from upstream source or, more usually, poured from pre-built binary bottles. Casks install pre-compiled applications (typically macOS GUI apps). Both are Ruby definitions hosted in Git repositories called taps; the official taps are homebrew/core (formulae) and homebrew/cask (casks).
Operationally, the things an administrator most needs to understand are:
- API mode — by default Homebrew reads
homebrew/coreandhomebrew/caskas a JSON API fromformulae.brew.shrather than cloning multi-hundred-megabyte Git repositories. This is the single biggest behavioural change in modern Homebrew. - The
HOMEBREW_*environment variables — Homebrew has no config file in the traditional sense; ~110 environment variables are the configuration surface (plus an optional system-wide/etc/homebrew/brew.env). - The security model — no-
sudopolicy, a build sandbox, bottle attestation verification, tap trust, and a family of "forbidden"/"allowed" allowlist variables for managed fleets.
The codebase is ~4.6 MB of Ruby across ~2,600 files, fully annotated with Sorbet type signatures and linted with custom RuboCop cops. The project is its own canonical documentation source: docs/Manpage.md is the authoritative command/flag reference and Library/Homebrew/env_config.rb is the authoritative environment-variable registry.
2. Architecture
The big picture
user types: brew install wget
│
▼
┌──────────────────────────────────────────────────────────┐
│ bin/brew (Bash launcher) │
│ • locates the prefix, sets HOMEBREW_* path vars │
│ • selects vendored or system Ruby │
│ • exec → Library/Homebrew/brew.rb │
└──────────────────────────────┬───────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Library/Homebrew/brew.rb (Ruby dispatcher) │
│ • parse ARGV → extract command + global flags │
│ • internal command? ── yes ─→ AbstractCommand subclass │
│ • external command? ── yes ─→ brew-<cmd>[.rb] / exec │
│ • fetch JSON API (unless HOMEBREW_NO_INSTALL_FROM_API) │
└──────────────────────────────┬───────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Command instance (cmd/<name>.rb or dev-cmd/<name>.rb) │
│ • CLI::Parser builds its option grammar │
│ • .run executes: │
│ Formulary / CaskLoader → Formula / Cask object │
│ → DownloadStrategy → UnpackStrategy → build/pour │
│ → Keg → symlink into prefix │
└──────────────────────────────────────────────────────────┘
Startup and dispatch
The entry point is the Bash script bin/brew, which establishes the environment (HOMEBREW_BREW_FILE, HOMEBREW_PREFIX, HOMEBREW_REPOSITORY, HOMEBREW_PATH, the cache and library paths), chooses Ruby (a vendored, relocatable Ruby unless a suitable system Ruby is available — see HOMEBREW_FORCE_VENDOR_RUBY), and hands off to Library/Homebrew/brew.rb.
brew.rb is the dispatcher. Reading its logic (Library/Homebrew/brew.rb):
- It scans
ARGVto separate the command from global help flags (-h,--help,--usage,help <cmd>). - It parses the remaining arguments with
Homebrew::CLI::Parser(allowing invalid options at this stage, since formula names look like arguments). - It resolves the command through
Commands:- Internal command (
Commands.valid_internal_cmd?/valid_internal_dev_cmd?) → look up the matchingHomebrew::AbstractCommandsubclass and call.new.run. - External Ruby v2 command → a tap-provided command that subclasses
AbstractCommand. - External Ruby command (
brew-<cmd>.rbonPATH) →required. - External executable (
brew-<cmd>onPATH) →exec'd directly.
- Internal command (
- Unless
HOMEBREW_NO_INSTALL_FROM_APIis set, it callsHomebrew::API.fetch_api_files!to refresh the JSON formula/cask data before running the command. - It reports the command run to analytics (unless disabled) and executes
command_instance.run.
A single top-level begin/rescue block in brew.rb defines the program's exit-code and error-reporting contract: UsageError prints help; BuildError reports the build failure (and suppresses issue reporting for non–Tier 1 configurations); Interrupt exits 130; deprecated-method and tap errors print an issue-reporting URL; everything else prints the error and exits 1.
Command architecture
Every built-in command lives in Library/Homebrew/cmd/<name>.rb (user commands) or Library/Homebrew/dev-cmd/<name>.rb (developer commands) and subclasses Homebrew::AbstractCommand. Each declares its option grammar in a cmd_args do ... end block that configures a Homebrew::CLI::Parser.
CLI::Parser (Library/Homebrew/cli/parser.rb) wraps Ruby's OptionParser and provides Homebrew's declarative option DSL:
switch— a boolean flag (optionally bound to an env var, withdepends_on,hidden, deprecation, and per-subcommand scoping).flag— an option that takes a value (--name=required,--nameoptional).comma_array— an option that parses a comma-separated list.named_args— declares the type/min/max of positional arguments (:formula,:cask,:tap,:command,:none, …).conflicts/depends_on— express mutual-exclusion and dependency constraints between options, enforced at parse time.cask_options/formula_options— mix in the global cask directory options, or allow formula-defined options to be parsed.
The four global options — -d/--debug, -q/--quiet, -v/--verbose, -h/--help — are added to every parser (Parser.global_options). Parsed arguments are frozen into a Homebrew::CLI::Args object; Homebrew::CLI::NamedArgs then resolves positional names into Formula, Cask or Keg objects via Formulary and Cask::CaskLoader, transparently handling the formula-vs-cask name collision (formulae win, with a warning, unless --formula/--cask is given).
Install pipeline (conceptual)
When you brew install <formula>, the resolved Formula object drives: dependency resolution → download (a DownloadStrategy picks curl/Git/etc. based on the URL) → unpack (an UnpackStrategy handles the archive type) → either pour a bottle or run the formula's install method in a sandbox → relocate and place the result as a keg in the Cellar → symlink (link) the keg into the prefix. Casks follow an analogous path through Cask::Installer and a set of artifact classes (app, pkg, binary, font, …), each knowing how to install and uninstall its artifact type.
3. Filesystem Layout
Homebrew is entirely contained in its prefix. The default prefix depends on the platform:
| Platform | Default prefix |
|---|---|
| macOS, Apple Silicon | /opt/homebrew |
| macOS, Intel | /usr/local |
| Linux | /home/linuxbrew/.linuxbrew |
Important
The default prefix is not cosmetic. Bottles are built for, and relocate to, these specific prefixes. Installing elsewhere forces source builds for nearly everything, which is slow and unsupported.
/opt/homebrewwas chosen on Apple Silicon precisely so it can coexist with a Rosetta 2/usr/localinstall.
Inside the prefix:
| Path | Contents |
|---|---|
bin/, sbin/ |
Symlinks to installed executables (this is what's on your PATH). |
Cellar/<name>/<version>/ |
Installed formula kegs — the real files. |
Caskroom/<token>/<version>/ |
Installed cask versions. |
opt/<name>/ |
Stable symlink to the active keg (used by other formulae). |
etc/, var/ |
Configuration and state for installed software. |
Library/Taps/<user>/homebrew-<repo>/ |
Cloned tap repositories. |
Library/Homebrew/ |
Homebrew's own Ruby source. |
.git/ |
Homebrew's Git checkout (this is the project). |
Outside the prefix:
| Path | Contents | Override |
|---|---|---|
| Download cache | macOS ~/Library/Caches/Homebrew, Linux ~/.cache/Homebrew |
HOMEBREW_CACHE |
| Logs | macOS ~/Library/Logs/Homebrew, Linux ~/.cache/Homebrew/Logs |
HOMEBREW_LOGS |
| Build temp | macOS /private/tmp, Linux /var/tmp |
HOMEBREW_TEMP |
| User config | ${XDG_CONFIG_HOME}/homebrew/ or ~/.homebrew/ |
(holds trust.json, livecheck_watchlist.txt, etc.) |
| System config | /etc/homebrew/brew.env |
System-wide environment defaults |
You can always discover these at runtime with brew --prefix, brew --cellar, brew --caskroom, brew --cache, brew --repository, and brew --taps.
4. Requirements & Platform Support
| Platform | Requirements |
|---|---|
| macOS | 64-bit Intel or Apple Silicon; latest macOS best supported. Requires Xcode Command Line Tools (xcode-select --install). |
| Linux | 64-bit x86_64 or ARM; glibc ≥ 2.13; gcc, make, and basic build tooling; a single-user (not shared) account. |
Homebrew uses a Support Tiers model: builds on non–Tier 1 configurations may fail by design, and brew.rb explicitly suppresses issue-reporting prompts for them (pointing instead at https://docs.brew.sh/Support-Tiers). Homebrew is designed for single-user installations and does not support shared/role-account or multi-user setups.
On Linux, Homebrew can simulate macOS code paths for auditing macOS formulae via HOMEBREW_SIMULATE_MACOS_ON_LINUX.
5. Installation & Deployment
Interactive install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Wire Homebrew into shells with brew shellenv (see the User Guide). On Linux this is mandatory; on macOS the default-prefix bin is usually already on PATH.
Fleet / MDM deployment
For managed deployments (MDM, Munki, Jamf), a few facilities matter:
brew as-console-user <command>runs abrewcommand as the active macOS console user even whenbrewis invoked asroot— the standard pattern for MDM workflows where management tooling runs as root but Homebrew must operate as the logged-in user. The nested command is dispatched throughHOMEBREW_BREW_FILE./etc/homebrew/brew.envsets system-wide environment defaults. WithHOMEBREW_SYSTEM_ENV_TAKES_PRIORITYset inside that file, it is loaded last so it overrides prefix and user settings — useful for enforcing fleet policy.HOMEBREW_FORCE_BREW_WRAPPERrequiresbrewto be invoked through a specific wrapper for non-trivial commands, letting you interpose policy/logging.brew bundlewith a checked-inBrewfileis the supported way to declare and converge the desired package set across machines.
Deployment checklist
- [ ] Install to the default prefix so bottles work.
- [ ] Add
brew shellenvoutput to the appropriate shell profile. - [ ] Pin auto-update behaviour for fleets (
HOMEBREW_NO_AUTO_UPDATEor a higherHOMEBREW_AUTO_UPDATE_SECS) so machines update on your schedule. - [ ] Set allowlist/denylist policy (
HOMEBREW_ALLOWED_TAPS,HOMEBREW_FORBIDDEN_*) where required. - [ ] Decide on analytics policy (
HOMEBREW_NO_ANALYTICS). - [ ] Distribute a
Brewfileand converge withbrew bundle install. - [ ] Verify with
brew configandbrew doctor.
6. Configuration Reference (Environment Variables)
Homebrew is configured almost entirely through HOMEBREW_* environment variables, defined authoritatively in Library/Homebrew/env_config.rb (the ENVS hash). Set them in shell profiles or in /etc/homebrew/brew.env.
Note
A variable must have a value to be detected —
export HOMEBREW_NO_INSECURE_REDIRECT=1, not a bareexport HOMEBREW_NO_INSECURE_REDIRECT. Boolean variables come in two flavours internally::settoggles enable on any non-empty value, while plain booleans also honour falsy values (false,no,off,nil,0).
Update & API behaviour
| Variable | Default | Effect |
|---|---|---|
HOMEBREW_NO_AUTO_UPDATE |
— | Skip auto-update before install/upgrade/tap. |
HOMEBREW_AUTO_UPDATE_SECS |
86400 | Minimum seconds between auto-updates. |
HOMEBREW_API_AUTO_UPDATE_SECS |
450 | Seconds between API data refreshes. |
HOMEBREW_FORCE_API_AUTO_UPDATE |
— | Refresh API data even if auto-update is disabled. |
HOMEBREW_NO_INSTALL_FROM_API |
— | Use local Git checkouts of core/cask taps instead of the JSON API. |
HOMEBREW_API_DOMAIN |
formulae.brew.sh/api |
Mirror for the JSON API. |
HOMEBREW_UPDATE_TO_TAG |
— | Always update to the latest stable tag, even after dev commands. |
HOMEBREW_NO_UPDATE_REPORT_NEW |
— | Don't list newly added formulae/casks after update. |
Download sources & mirrors
| Variable | Default | Effect |
|---|---|---|
HOMEBREW_BOTTLE_DOMAIN |
ghcr.io/v2/homebrew/core |
Mirror for bottle downloads. |
HOMEBREW_ARTIFACT_DOMAIN |
— | Prefix all download URLs (bottle URLs have their domain replaced). |
HOMEBREW_ARTIFACT_DOMAIN_NO_FALLBACK |
— | Error instead of falling back when the artifact domain fails. |
HOMEBREW_BREW_GIT_REMOTE |
official | Git remote for Homebrew/brew. |
HOMEBREW_CORE_GIT_REMOTE |
official | Git remote for homebrew-core. |
HOMEBREW_PIP_INDEX_URL |
pypi.org/simple |
Index for PyPI resource downloads. |
HOMEBREW_DOCKER_REGISTRY_TOKEN / ..._BASIC_AUTH_TOKEN |
— | Auth for a registry proxying GitHub Packages. |
Network, proxies & TLS
| Variable | Effect |
|---|---|
http_proxy / https_proxy / all_proxy / ftp_proxy / no_proxy |
Standard proxy settings honoured by curl/git/svn. |
HOMEBREW_NO_INSECURE_REDIRECT |
Forbid HTTPS→HTTP redirects. |
HOMEBREW_CURL_RETRIES (3) |
Retry count passed to curl. |
HOMEBREW_CURLRC |
Pass a curl config / don't disable .curlrc. |
HOMEBREW_FORCE_BREWED_CURL / ..._GIT / ..._CA_CERTIFICATES |
Use Homebrew's own curl/git/CA certs (auto-set if the system version is too old). |
HOMEBREW_SSH_CONFIG_PATH |
Use a custom SSH config for Git-over-SSH fetches. |
Build behaviour
| Variable | Default | Effect |
|---|---|---|
HOMEBREW_MAKE_JOBS |
CPU cores | Parallel make jobs. |
HOMEBREW_DOWNLOAD_CONCURRENCY |
auto (2× cores) | Parallel download connections (1 = serial). |
HOMEBREW_ARCH |
native | Linux -march value. |
HOMEBREW_CC (via --cc) |
— | Override the compiler. |
HOMEBREW_FORMULA_BUILD_NETWORK |
allow | Sandbox network policy during build (allow/deny). |
HOMEBREW_FORMULA_POSTINSTALL_NETWORK |
allow | …during post-install. |
HOMEBREW_FORMULA_TEST_NETWORK |
allow | …during brew test. |
HOMEBREW_FAIL_LOG_LINES (15) |
— | Lines of output shown on a system failure. |
HOMEBREW_DISPLAY_INSTALL_TIMES |
— | Print per-formula install times. |
HOMEBREW_KEEP_TMP (via --keep-tmp) |
— | Retain build temp files. |
Cleanup & autoremove
| Variable | Default | Effect |
|---|---|---|
HOMEBREW_NO_INSTALL_CLEANUP |
— | Never auto-cleanup after installs. |
HOMEBREW_NO_CLEANUP_FORMULAE |
— | Comma-separated formulae to never clean up/autoremove. |
HOMEBREW_CLEANUP_MAX_AGE_DAYS |
120 | Age threshold for cached downloads. |
HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS |
30 | Days between full periodic cleanups. |
HOMEBREW_NO_AUTOREMOVE |
— | Don't auto-remove unused dependents on cleanup/uninstall. |
Cask & upgrade behaviour
| Variable | Effect |
|---|---|
HOMEBREW_CASK_OPTS |
Default options appended to all cask commands (--*dir, --language, --require-sha, --no-binaries). |
HOMEBREW_UPGRADE_GREEDY |
Pass --greedy to all cask upgrades. |
HOMEBREW_UPGRADE_GREEDY_CASKS |
Space-separated casks treated as --greedy. |
HOMEBREW_UPGRADE_AUTO_UPDATES_CASKS |
Auto-upgrade auto_updates true casks (default with HOMEBREW_DEVELOPER; default in 5.2.0). |
HOMEBREW_NO_UPGRADE_QUIT_CASKS |
Don't quit running apps during cask upgrades. |
HOMEBREW_FORBID_CASKS |
Refuse to install any casks. |
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK |
Skip the post-install broken-linkage/outdated-dependents check. |
HOMEBREW_NO_INSTALL_UPGRADE |
brew install <x> won't upgrade an outdated <x>. |
Policy & allowlists (fleet-relevant)
| Variable | Effect |
|---|---|
HOMEBREW_ALLOWED_TAPS |
Only install formulae from official taps or this space-separated list. |
HOMEBREW_FORBIDDEN_FORMULAE / ..._CASKS / ..._TAPS / ..._LICENSES |
Refuse to install anything (or any dependency) matching these lists. |
HOMEBREW_FORBIDDEN_CASK_ARTIFACTS |
Forbid specific cask artifact types (e.g. pkg installer). |
HOMEBREW_FORBIDDEN_OWNER / ..._OWNER_CONTACT |
Identify who set the forbidden policy (shown in errors). |
HOMEBREW_FORBID_PACKAGES_FROM_PATHS |
Refuse to read formulae/casks from local file paths (default true unless HOMEBREW_DEVELOPER). |
HOMEBREW_REQUIRE_TAP_TRUST / HOMEBREW_NO_REQUIRE_TAP_TRUST |
Require (or not) brew trust for non-official taps before loading them. |
HOMEBREW_DISABLE_LOAD_FORMULA |
Refuse to load formulae at all (for untrusted contexts like PRs). |
Security & provenance
| Variable | Effect |
|---|---|
HOMEBREW_VERIFY_ATTESTATIONS / HOMEBREW_NO_VERIFY_ATTESTATIONS |
Verify (or skip) cryptographic build-provenance attestations for homebrew-core bottles via gh. |
HOMEBREW_SANDBOX_LINUX / HOMEBREW_NO_SANDBOX_LINUX |
Enable/disable the bwrap Linux build sandbox (default with HOMEBREW_DEVELOPER; default in 5.2.0). |
HOMEBREW_NO_SANDBOX_CASK |
Disable sandboxing for cask artifacts that run executables. |
HOMEBREW_SUDO_THROUGH_SUDO_USER |
Use $SUDO_USER when Homebrew must call sudo. |
HOMEBREW_GITHUB_API_TOKEN / HOMEBREW_GITHUB_PACKAGES_TOKEN / ..._USER |
Tokens for the GitHub API and Packages registry. |
Output & UX
| Variable | Effect |
|---|---|
HOMEBREW_NO_ANALYTICS |
Disable anonymous analytics. |
HOMEBREW_NO_COLOR / HOMEBREW_COLOR |
Force colour off / on. |
HOMEBREW_NO_EMOJI |
Suppress the success badge. |
HOMEBREW_INSTALL_BADGE (🍺) |
The success badge text. |
HOMEBREW_NO_ENV_HINTS |
Suppress env-var hints. |
HOMEBREW_VERBOSE / HOMEBREW_DEBUG / HOMEBREW_VERBOSE_USING_DOTS |
Always-verbose / always-debug / dotted progress. |
HOMEBREW_EDITOR / HOMEBREW_BROWSER / HOMEBREW_DISPLAY |
Editor / browser / X11 display for edit/home. |
HOMEBREW_BAT / HOMEBREW_BAT_THEME / HOMEBREW_BAT_CONFIG_PATH |
Use bat for brew cat. |
Developer mode
| Variable | Effect |
|---|---|
HOMEBREW_DEVELOPER |
Turn warnings into errors and enable developer-friendly defaults (sandbox, attestations, ask mode, etc.). |
HOMEBREW_NO_INSTALL_FROM_API |
(See above) required to edit core formulae locally. |
HOMEBREW_SORBET_RUNTIME / HOMEBREW_SORBET_RECURSIVE |
Enable Sorbet runtime typechecking. |
HOMEBREW_PRY / HOMEBREW_DISABLE_DEBREW |
Use Pry for brew irb / disable the --debug formula debugger. |
HOMEBREW_GIT_NAME / HOMEBREW_GIT_EMAIL / ..._COMMITTER_* |
Git author/committer identity for PR commands. |
Tip
This table is curated for operators. The exhaustive list — including the many
HOMEBREW_BUNDLE_*toggles and several deprecated transitional flags — lives inLibrary/Homebrew/env_config.rband is rendered into the ENVIRONMENT section ofman brew.
7. Security Model
Homebrew's security posture is built around the principle that build scripts are untrusted code and should never run with elevated privileges.
No sudo
Homebrew refuses to run as root. Because the prefix is owned by the installing user, no privilege escalation is needed to install software. A formula's make/configure scripts run arbitrary code; running them as root would let a malicious or buggy script modify anything on the system. The macOS sandbox blocks much of this, but the sandbox doesn't function as root — hence the hard no-sudo rule. The single exception is sudo brew services, where only the resulting service runs elevated.
Build sandbox
- macOS uses
sandbox-execto confine builds. - Linux uses
bwrap(Bubblewrap) whenHOMEBREW_SANDBOX_LINUXis set (default underHOMEBREW_DEVELOPER; becoming the default in 5.2.0). - Per-phase network access is controllable independently for build, post-install and test via
HOMEBREW_FORMULA_BUILD_NETWORK/..._POSTINSTALL_NETWORK/..._TEST_NETWORK(eachallow/deny), overriding formula DSL settings. - Cask artifacts that execute binaries are also sandboxed unless
HOMEBREW_NO_SANDBOX_CASKis set.
Bottle provenance (attestations)
For homebrew-core bottles, Homebrew can verify cryptographic build-provenance attestations using the gh CLI (HOMEBREW_VERIFY_ATTESTATIONS; suppress with HOMEBREW_NO_VERIFY_ATTESTATIONS). This confirms a bottle was built by Homebrew's CI and not tampered with.
Tap trust
Third-party taps run code on your machine. Homebrew is introducing a tap-trust mechanism: with HOMEBREW_REQUIRE_TAP_TRUST set, non-official tap formulae, casks and commands must be explicitly trusted with brew trust before they load. Trust entries are stored in ${XDG_CONFIG_HOME}/homebrew/trust.json (or ~/.homebrew/trust.json). brew untrust revokes them. This supersedes the older HOMEBREW_EVAL_ALL model.
Allowlists & denylists for managed environments
Administrators can constrain what Homebrew will install:
HOMEBREW_ALLOWED_TAPS— refuse anything not in an official tap or this list.HOMEBREW_FORBIDDEN_FORMULAE/_CASKS/_TAPS/_LICENSES/_CASK_ARTIFACTS— refuse matching packages, dependencies, licenses or artifact types.HOMEBREW_FORBID_CASKS— block all casks.HOMEBREW_FORBID_PACKAGES_FROM_PATHS— block installing formulae/casks from arbitrary local paths.HOMEBREW_DISABLE_LOAD_FORMULA— refuse to evaluate formula Ruby entirely (e.g. when reviewing untrusted PRs).
Ownership & permissions
On macOS, Homebrew-managed files default to the installing user and the admin group; on Linux, to the installing user and their group. Directory/file permissions default to 0755 — only the owner can modify files in the prefix, while all users may read and execute installed binaries. Installed binaries run with the privileges of whoever launches them.
Warning
Homebrew is a single-user system. Multi-user / shared-account configurations are unsupported and have real security and correctness pitfalls. Don't
chown rootthe prefix.
8. Operations
Health & diagnostics
brew doctor # check for problems (non-zero exit if any found)
brew doctor --list-checks # list every diagnostic check
brew config # full system + Homebrew config (attach to bug reports)
brew missing # check installed kegs for missing dependencies
brew --version # versions of brew + tapped core/cask
Updating Homebrew itself
brew update # fetch newest brew + formula/cask data, run migrations
brew update --force # force a full update check
brew update-reset # hard-reset all Homebrew Git repos to origin/HEAD
Caution
brew update-resetdestroys all uncommitted and committed local changes in Homebrew's repositories. Use it to recover a corrupted checkout, not casually.
Logs and build failures
brew --cache # download cache location
brew --prefix/opt/... # locate installed software
brew gist-logs <formula> # upload a failed build's logs to a Gist
HOMEBREW_LOGS controls where logs are written; HOMEBREW_FAIL_LOG_LINES controls how much output is shown on a system failure.
Backups & migration
The supported backup unit is the Brewfile:
brew bundle dump --global --describe # snapshot to ~/.Brewfile
# ... on the new machine, after installing Homebrew:
brew bundle install --global
Because the prefix is self-contained, you can also archive the prefix itself, but a Brewfile + fresh install is cleaner and avoids relocating absolute paths.
Developer mode
brew developer on # warnings → errors; track main instead of stable tags
brew developer off # back to stable behaviour
brew developer state
Running a developer command (e.g. brew edit, brew create) implicitly enables developer mode, which also makes auto-update run more frequently and track the main branch. Reset with brew developer off (or keep stable tags with HOMEBREW_UPDATE_TO_TAG=1).
9. The Formula & Cask Model
Formulae
A formula is a Ruby class (a subclass of Formula) that declares a package's source URL, checksum, dependencies, and an install method. The canonical reference for authoring is the Formula Cookbook; the key operational facts:
- Formulae normally install a bottle (pre-built binary). Bottles are hosted on GitHub Packages (
ghcr.io) and are relocatable to the standard prefixes. - A formula installs into a keg (
Cellar/<name>/<version>) and is then linked (symlinked) into the prefix.brew link/brew unlinkcontrol this; keg-only formulae are intentionally never linked. - Dependencies have kinds: required, recommended, optional, build, test, and implicit.
brew deps/brew usesexpose the graph; the--include-*/--skip-recommendedoptions select which kinds to traverse. - Formulae can define
service do ... endblocks consumed bybrew services(rendered to a launchd plist or systemd unit).
Casks
A cask is a Ruby definition (in Library/Homebrew/cask/) describing a pre-built application and its artifacts. Each artifact type — app, pkg, binary, font, colorpicker, service, qlplugin, and ~20 more — is a class under cask/artifact/ that knows how to install and uninstall itself, where to put files (the --*dir options), and how to clean up (uninstall, zap). Casks marked auto_updates true or version :latest interact specially with brew upgrade (see the User Guide FAQ).
API mode vs. local taps
By default Homebrew serves homebrew/core and homebrew/cask as a JSON API (Homebrew::API, data from formulae.brew.sh/api), avoiding the cost of cloning these very large repositories. brew install resolves formula/cask metadata from this JSON. Setting HOMEBREW_NO_INSTALL_FROM_API=1 reverts to full local Git checkouts — slower, larger, but required when you want to brew edit core formulae or run them against local changes.
10. Developer Commands
Homebrew ships ~60 developer commands (in Library/Homebrew/dev-cmd/) for people who write and maintain formulae and casks. They are hidden from the everyday command list but documented in man brew under DEVELOPER COMMANDS. The most important:
Authoring & editing
| Command | Purpose |
|---|---|
create <url> |
Generate a new formula from a source URL. |
edit [<formula>|<cask>|<tap>] |
Open a formula/cask/tap in $HOMEBREW_EDITOR. |
cat <formula> |
Print a formula/cask's source. |
extract <formula> <tap> |
Extract a specific version of a formula into a tap. |
tap-new <user>/<repo> |
Scaffold a new tap repository. |
Auditing & style
| Command | Purpose |
|---|---|
audit |
Check a formula/cask for style and correctness violations (run before submitting). |
style |
Run RuboCop style checks on formulae/casks/taps. |
typecheck (tc) |
Run Sorbet type checking. |
readall |
Import all formulae/casks to catch Ruby errors. |
linkage |
Check a keg's library linkage. |
Building & bottling
| Command | Purpose |
|---|---|
bottle |
Generate a bottle from an installed keg. |
unbottled |
List formulae without bottles. |
unpack |
Unpack a formula's source into a directory. |
test |
Run a formula's test block. |
dispatch-build-bottle |
Trigger CI bottle builds. |
Pull-request workflow
| Command | Purpose |
|---|---|
bump / bump-formula-pr / bump-cask-pr / bump-revision |
Update versions and open PRs. |
bump-unversioned-casks |
Bump casks without explicit versions. |
pr-pull / pr-publish / pr-automerge / pr-upload |
Maintainer PR/bottle-publishing pipeline. |
release |
Cut a Homebrew/brew release. |
contributions |
Report contribution statistics. |
Maintenance & introspection
| Command | Purpose |
|---|---|
livecheck (lc) |
Check whether newer upstream versions exist. |
update-python-resources / update-perl-resources |
Regenerate language-specific resource stanzas. |
generate-formula-api / generate-cask-api / generate-man-completions |
Regenerate the JSON API, man page and completions. |
irb / ruby / sh / prof / debugger |
Interactive Ruby console, ad-hoc Ruby, build-env shell, profiler, debugger. |
tests |
Run Homebrew's own test suite. |
vendor-gems / install-bundler-gems |
Manage Homebrew's Ruby gem dependencies. |
Note
Most developer commands require a local tap checkout (
HOMEBREW_NO_INSTALL_FROM_API=1+brew tap --force homebrew/core) because they read and write formula Ruby files directly.
11. Codebase Internals
Languages & structure
The codebase is Ruby, organized under Library/Homebrew/:
| Directory | Responsibility |
|---|---|
cmd/ |
Built-in user commands (88 files). |
dev-cmd/ |
Developer commands (58 files). |
cli/ |
The CLI parser, args, named-args resolution, and error types. |
cask/ |
Cask DSL, loader, installer, and artifact classes. |
services/ |
brew services implementation (launchd/systemd). |
bundle/ |
brew bundle / Brewfile implementation. |
api/ |
JSON API client for core/cask data. |
download_strategy/, unpack_strategy/ |
Pluggable download and archive handlers. |
extend/, extend/os/ |
Core-class extensions and OS-specific (mac/linux) overrides. |
utils/ |
Shared utilities (output, GitHub, bottles, AST, …). |
rubocops/ |
Custom RuboCop cops that enforce formula/cask conventions. |
sorbet/ |
Sorbet RBI type stubs and configuration. |
shims/ |
Compiler/tool shims used during sandboxed builds. |
test/ |
The RSpec test suite. |
OS abstraction
Cross-platform behaviour is handled by the extend/os/ tree: a base implementation lives in the main module and extend/os/mac/ or extend/os/linux/ override OS-specific pieces (e.g. services/cli.rb branches on System.launchctl? vs System.systemctl?). This keeps the bulk of the code OS-agnostic with thin platform shims.
Type safety & conventions
- Every file carries a Sorbet
# typed:sigil (strict,true, etc.) and# frozen_string_literal: true. - Methods are annotated with
sig { ... }signatures;brew typecheckruns Sorbet. - Homebrew's own RuboCop cops (
rubocops/) enforce formula/cask DSL conventions on top of standard style rules. - Generated Sorbet RBI files (
sorbet/rbi/) describe the dynamically-defined command and DSL methods.
Ruby runtime
Homebrew ships a vendored, relocatable Ruby so it doesn't depend on the system Ruby (which Apple has been removing). HOMEBREW_FORCE_VENDOR_RUBY forces its use; HOMEBREW_USE_RUBY_FROM_PATH (build-time) and setup-ruby manage the alternative. Non-vendored gems are declared in Library/Homebrew/Gemfile in optional groups (doc, style, tests, typecheck, prof, …); a handful are vendored unconditionally (addressable, concurrent-ruby, plist, ruby-macho, sorbet-runtime, …).
12. Testing & Quality
| Layer | Tool | How to run |
|---|---|---|
| Unit & integration tests | RSpec (test/) |
brew tests |
| Type checking | Sorbet | brew typecheck (tc) |
| Style | RuboCop + custom cops | brew style |
| Formula/cask audit | brew audit |
brew audit --strict <formula> |
| Load-all sanity | brew readall |
brew readall |
| CI bottle builds | brew test-bot |
(CI) |
Runtime Sorbet typechecking can be forced with HOMEBREW_SORBET_RUNTIME (set automatically during brew test/brew tests). For contributors, the recommended first task is to run brew audit --strict <some-formula> and fix what it reports.
Tip
Homebrew's contribution culture is mature: read
CONTRIBUTING.mdand the Formula/Cask Cookbooks, runbrew audit --strictandbrew stylebefore submitting, and usebrew bump-formula-pr/brew bump-cask-prto open version-bump PRs in the correct shape.
13. Troubleshooting (Operational)
brew insists on the Command Line Tools
Install or repair them: xcode-select --install. Required again after every major macOS upgrade — follow with brew upgrade.
Corrupted Homebrew checkout / "untracked working tree files" on update
cd "$(brew --repository)"
git reset --hard FETCH_HEAD
cd "$(brew --repository)/Library"
git clean -fd
For a complete reset to upstream across all Homebrew repos: brew update-reset (destroys local changes).
Install fails with unknown revision or path not in the working tree
A network problem between the machine and GitHub during the initial clone, not a Homebrew bug. Try a wired connection or VPN, or disable network-monitoring tools. (Common in restricted corporate networks — pair with the proxy variables.)
Linkage broken after a macOS upgrade
xcode-select --install
brew upgrade
brew missing and brew linkage <formula> help identify what broke.
Dual installations after Migration Assistant
A copied Intel /usr/local Homebrew on an Apple Silicon Mac. Confirm arch prints arm64, reinstall under /opt/homebrew, restore with brew bundle, then uninstall the old /usr/local install with the official uninstaller (--path=/usr/local).
Cask permission denied / metadata loss on upgrade
Grant the terminal App Management or Full Disk Access so Homebrew can do in-place cask upgrades; otherwise it falls back to uninstall/reinstall and macOS forgets Dock/Launchpad position and permissions. Fix Caskroom ownership with sudo chown -R "$(whoami)" "$(brew --caskroom)".
14. Potential Improvements
Several transitions are visible in the source and are worth tracking when planning upgrades:
- Homebrew 5.2.0 defaults shift. Multiple variables are documented as "becoming the default in 5.2.0": the Linux
bwrapsandbox (HOMEBREW_SANDBOX_LINUX), auto-upgradingauto_updates truecasks (HOMEBREW_UPGRADE_AUTO_UPDATES_CASKS), SBOM generation (HOMEBREW_SBOM), and severalHOMEBREW_BUNDLE_*describe/jobs/secrets behaviours. Audit any scripts that depend on today's defaults. - Tap-trust supersedes
HOMEBREW_EVAL_ALL.HOMEBREW_EVAL_ALLis deprecated in favour ofHOMEBREW_REQUIRE_TAP_TRUST/HOMEBREW_NO_REQUIRE_TAP_TRUSTandbrew trust. Migrate fleet policy accordingly. - Internal API beta.
HOMEBREW_USE_INTERNAL_APItoggles a new beta path for fetching formula/cask data, slated to become default. --askbecoming default.HOMEBREW_ASKis documented to become the default behaviour, so installs will print a plan and prompt by default; setHOMEBREW_NO_ASKwhere non-interactive behaviour is required (CI/automation).- Several
odeprecated/hiddenflags are marked for removal in 5.2.0 (e.g.HOMEBREW_CASK_OPTS_BINARIES,HOMEBREW_NO_UPGRADE_AUTO_UPDATES_CASKS). Replace them with the supportedHOMEBREW_CASK_OPTS/ cask-options forms.
These are transitional concerns inherent to a mature, continuously-released tool, not defects.
15. Quick Reference
Key paths
brew --prefix # install root (/opt/homebrew, /usr/local, /home/linuxbrew/.linuxbrew)
brew --cellar # Cellar (installed formula kegs)
brew --caskroom # Caskroom (installed casks)
brew --cache # download cache
brew --repository # Homebrew's own Git checkout
brew --taps # Taps directory
Operations cheat-sheet
brew doctor # diagnose
brew config # system info for bug reports
brew update && brew upgrade # update everything
brew update-reset # hard-reset Homebrew repos (destructive)
brew bundle dump --global # snapshot installed packages
brew bundle install --global # restore them
sudo brew services start <name> # system-wide service at boot
brew as-console-user <command> # run as the console user (MDM)
High-value configuration
export HOMEBREW_NO_AUTO_UPDATE=1 # control update timing (fleets)
export HOMEBREW_NO_ANALYTICS=1 # disable analytics
export HOMEBREW_NO_INSTALL_FROM_API=1 # use local taps (formula editing)
export HOMEBREW_ALLOWED_TAPS="..." # restrict installable taps
export HOMEBREW_FORBIDDEN_LICENSES="GPL-3.0" # license policy
export HOMEBREW_VERIFY_ATTESTATIONS=1 # verify bottle provenance
Authoritative source files
| File | What it defines |
|---|---|
docs/Manpage.md |
Every command and flag (rendered to man brew). |
Library/Homebrew/env_config.rb |
Every HOMEBREW_* environment variable. |
Library/Homebrew/brew.rb |
Command dispatch and the global error/exit contract. |
Library/Homebrew/cli/parser.rb |
The option-parsing DSL. |
Library/Homebrew/cmd/ & dev-cmd/ |
Command implementations. |