1. 为什么 Homebrew 更新比安装更容易踩坑
很多人在 macOS 上装完 Homebrew 之后,第一件事就是跑 brew update。结果卡在 Updating Homebrew... 的时间足够泡完一包泡面,甚至直接报 git fetch 错误。如果你常年混国内网络,大概率还会遇到一个更常见的死循环:更新失败,去搜镜像源设置,配完镜像源之后重启终端再更新,还是失败。这不是因为你操作有问题,而是因为 Homebrew 的"更新"和你以为的"更新"可能不是同一件事。
先说结论:Homebrew 更新和镜像源设置这两件事,本质是在解决同一个问题——怎么让 brew 主程序、公式仓库、二进制 bottle 包这三条链路都走得通。光改一个 HOMEBREW_BOTTLE_DOMAIN 往往只解决了下载安装包慢的问题,但 brew update 卡住通常和公式仓库的 git 远程地址有关。所以这篇文章我不打算只给你一段复制粘贴的配置,而是把更新机制、环境变量、常见报错一条条拆开讲,确保你下次遇到问题不用再到处翻帖子。
1.1 先分清三个"更新":brew update、brew upgrade 和自动更新
Homebrew 里有三个非常容易混淆的概念:
brew update:更新 Homebrew 自身以及 formula/cask 的索引数据。它不会升级你安装的软件,只更新"有哪些新版本可用"这份清单。brew upgrade:根据brew update拿到的清单,真正升级你已经安装的软件包。- 自动更新:当你在执行
brew install、brew tap等命令时,Homebrew 会默认先自动跑一次brew update,这是很多人觉得"安装个东西怎么这么慢"的主要原因。
所以如果你发现 brew install 很慢,不一定是下载包慢,很可能是前面那层自动更新在拖后腿。临时关掉自动更新的环境变量是 HOMEBREW_NO_AUTO_UPDATE=1。设置之后,每次安装前不会再自动更新,适合你只想立刻装一个包的时候。但从长期健康角度,我建议还是定期手动 brew update,别完全关掉。
brew upgrade 里还有一个容易忽略的 Cask 更新问题。图形应用的更新策略和命令行工具不同,很多 Cask 默认不做无条件升级,需要加 --greedy 才会把所有 Cask 都拉一遍检查更新。日常我会用 brew upgrade --greedy 把 CLI 工具和图形应用一起处理,省得每次还要分开跑。
1.2 更新 Homebrew 时到底在更新什么
要理解为什么镜像源配置那么重要,得先知道 Homebrew 的更新涉及哪几部分。当前 Homebrew 的主要数据链路大致是这样的:
- brew 主程序仓库:核心脚本和命令逻辑,对应一个 git 仓库。
- homebrew-core 仓库:各类命令行工具的 formula 定义。新版本 Homebrew 默认通过 API 获取这部分数据,不一定需要完整 clone。
- homebrew-cask 仓库:图形应用的 Cask 定义。
- bottle 下载地址:预编译的二进制安装包,通常是
.tar.gz或.bottle.tar.gz。
其中 brew update 主要拉取主程序和公式索引。新版本 Homebrew 默认会从 formulae.brew.sh 这类 API 拿数据,旧版本或某些切换了配置的环境则还依赖 git 仓库。也就是说,你更新的速度取决于网络到这几个上游源的速度。如果你在上海、广东这类出口带宽还算可以的地方,可能体感不明显;但如果是内网、教育网或者网络波动较大的场景,直接访问这些源就会频繁超时。
我看到很多教程只教你配 HOMEBREW_BOTTLE_DOMAIN,结果 brew install 确实变快了,但 brew update 还是卡死,原因就在这里:bottle 镜像只管下载安装包,不管 git 拉取和 API 拉取。所以要彻底解决更新问题,需要把 brew 主程序远程地址、core 仓库地址、API 地址、bottle 地址一起换到同一家镜像源。
1.3 什么人最需要用镜像源
我接触过的用户里,最需要配置镜像源的不是那些偶尔用一次 Homebrew 的人,而是以下三类:
- 日常开发和 CI 流水线重度依赖 Homebrew 的人,每次都等更新会很崩溃。
- 校园网或单位网络环境下,出口访问 GitHub 相关资源不稳定。
- 刚买 Mac 准备装开发环境的新手,因为一上来就遇到失败,很容易被劝退。
如果你是这三类人之一,下面的配置流程建议完整看完。哪怕你现在网络很好,把配置先写在 profile 里,以后网络一抽风也能应急。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 更新前的环境诊断:别让配置白做
镜像源配置本身不复杂,但很多人配完发现不生效,问题往往出在环境诊断做少了。Homebrew 的常见安装路径有两种:Apple Silicon 上默认是 /opt/homebrew,Intel Mac 上默认是 /usr/local。如果你装过 Deepin、Windows 子系统或者自定义目录,路径可能完全不同。镜像源配置里的很多变量是相对 brew 根目录和 shell 环境变量来的,所以第一步不是复制命令,而是先确认你当前的 Homebrew 长什么样。
2.1 确认安装方式和架构
在终端执行:
bash复制which brew
brew --prefix
brew --version
如果 which brew 显示 /opt/homebrew/bin/brew,说明是 Apple Silicon 默认安装;显示 /usr/local/bin/brew,说明是 Intel 或通过 Rosetta 安装;如果显示别的路径,比如某些脚本安装到了 ~/homebrew,那就需要额外注意。
brew --version 的输出里会看到 Homebrew 版本号。如果你发现版本还是 3.x 以下,说明电脑上的 Homebrew 已经比较旧了。新版 Homebrew 对 HOMEBREW_API_DOMAIN 这类变量支持更完善,旧版本可能需要先处理 git 仓库结构的问题。
另外建议顺手执行一次:
bash复制brew config
这个命令会输出大量信息,包括 HOMEBREW_PREFIX、HOMEBREW_REPOSITORY、HOMEBREW_SHELLENV_PREFIX,以及你是否设置了 HOMEBREW_* 环境变量。这一步非常关键,很多"配了镜像源没生效"的人,在这里就能看出问题:要么变量根本没导出,要么导出到了错误的 shell 配置文件里。
2.2 确认当前 shell 与配置文件
Homebrew 镜像源变量一般写入 shell 配置文件,但你得知道你用的是哪种 shell。macOS 从 Catalina 开始默认 zsh,大多数新用户应该改 ~/.zshrc;如果你自己切到过 bash,或者用的是 fish,那配置文件就不一样。写错文件的结果是:你关掉终端再打开,配置全部丢失,但当前会话里又看起来是正常的,特别容易蒙人。
确认方式:
bash复制echo $SHELL
如果是 /bin/zsh,编辑 ~/.zshrc;如果是 /bin/bash,编辑 ~/.bash_profile 或 ~/.bashrc;fish 的话则是 ~/.config/fish/config.fish。建议改完之后不要只执行 source,而是重新开一个终端标签页,再执行 env | grep HOMEBREW 确认变量真的进去了。
2.3 建议先把 brew 恢复到可诊断状态
如果你已经之前改了一堆乱七八糟的变量,先不要急着继续叠加。我见过不少用户同时配置了官方源、清华源、中科大源,还在 ~/.curlrc 里写了奇怪的参数。这种状态下排查起来非常痛苦。我的建议是先把当前所有相关变量清干净:
bash复制env | grep HOMEBREW
看到有 HOMEBREW_BREW_GIT_REMOTE、HOMEBREW_CORE_GIT_REMOTE、HOMEBREW_BOTTLE_DOMAIN、HOMEBREW_API_DOMAIN 这些变量,先注释掉或删掉,然后重新打开终端。等环境干净了,再判断是网络问题还是配置问题。
这一步还有一个好处:如果干净环境下 brew update 能跑通,说明之前的问题大概率是配置冲突或者变量值写错了,不用瞎折腾。
3. 国内镜像源的完整配置流程
现在正式进入镜像源设置。我以当前主流的清华 TUNA 和中科大镜像为例。两家我都实际用过,整体稳定性都在线。清华源的更新频率和覆盖范围比较全,中科大源在部分教育网场景下速度更好。你不需要同时配两家,选一个顺手、稳定的就行。最忌讳的做法是 brew 主程序用清华源,core 仓库却用中科大源,跨镜像混用会导致哈希值对不上,最后还是一堆报错。
3.1 镜像源配置的核心变量
先通过表格把关键变量说清楚,后面遇到问题能对症下药:
| 环境变量 | 作用 | 不配置时的默认行为 |
|---|---|---|
HOMEBREW_BREW_GIT_REMOTE |
brew 主程序 git 远程地址 | 官方 GitHub 上的 brew.git |
HOMEBREW_CORE_GIT_REMOTE |
homebrew-core 公式仓库 git 远程地址 | 官方 GitHub 上的 homebrew-core.git |
HOMEBREW_BOTTLE_DOMAIN |
二进制预编译包的下载域名 | 官方 ghcr.io 或 GitHub Releases |
HOMEBREW_API_DOMAIN |
获取 formula/cask 索引 API 的域名 | 官方 formulae.brew.sh |
HOMEBREW_INSTALL_FROM_API |
是否通过 API 获取公式信息,而不是 clone 整个 core 仓库 | 新版默认开启 |
很多老教程只写了前三项,没有 HOMEBREW_API_DOMAIN。但新版 Homebrew 的 brew update 会频繁请求 API,不配这个变量,你会在更新时看到大量请求卡在 formulae.brew.sh 上。这也是"配了瓶装镜像但还是慢"的最常见原因之一。
3.2 用清华 TUNA 镜像源完整替换
编辑你的 shell 配置文件,在末尾追加:
bash复制export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git"
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git"
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"
export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api"
export HOMEBREW_INSTALL_FROM_API=1
然后执行:
bash复制source ~/.zshrc
注意,上面假设你用的是 zsh;如果你用的是 bash,后续所有 source 命令都改成对应的配置文件。
配置完成后再验证环境变量:
bash复制env | grep HOMEBREW
确认四个关键变量都在,然后执行:
bash复制brew update
第一次切到镜像源时,brew 可能会重新拉取或校验仓库状态,耗时不一定很短,但之后的更新会明显快很多。如果你在更新过程中看到 remote: Enumerating objects 这类输出走得很慢,可以再等一小会儿。
3.3 中科大镜像源备选方案
清华源有时候也会抽风,特别是大面积并发更新时。备选中科大源的做法是一模一样的,只需要把域名换掉:
bash复制export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.ustc.edu.cn/brew.git"
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.ustc.edu.cn/homebrew-core.git"
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.ustc.edu.cn/homebrew-bottles"
export HOMEBREW_API_DOMAIN="https://mirrors.ustc.edu.cn/homebrew-bottles/api"
export HOMEBREW_INSTALL_FROM_API=1
中科大源在部分高校网络里表现不错,但如果你所在的网络访问清华更快,就继续用清华。这里的关键不是哪家绝对第一,而是别同时混用。
如果你之前已经 clone 过完整的仓库,希望在命令行里直接改掉已有 remote,也可以用 git remote set-url 的方式。比如:
bash复制cd "$(brew --repo)"
git remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git
但一般情况下,设置环境变量就足够覆盖了,不需要手动改每个仓库的 remote。
3.4 配置后如何验证是否生效
验证镜像源不是光看环境变量有没有设置,而是要实际验证下载请求真的走了镜像。最直观的方法是看 brew config 里的输出。执行:
bash复制brew config | grep -i homebrew
如果看到类似 HOMEBREW_BREW_GIT_REMOTE: https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git、HOMEBREW_BOTTLE_DOMAIN: https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles 的内容,说明配置已经生效。
更接近真实场景的验证方式是安装一个体积较大的、带 bottle 的软件,比如:
bash复制brew install wget
如果你开着网络监控或者 Charles 这类抓包工具,会看到请求都发向了清华源。即使不开抓包,安装过程明显变快,也能侧面说明镜像生效了。
还需要留一个心眼:Homebrew 不是所有资源都走镜像,比如某些 cask 的 app 下载地址是应用官网本身,这些地址不受 HOMEBREW_BOTTLE_DOMAIN 控制。所以如果安装某个图形应用时依然很慢,不要第一时间怀疑镜像没生效,先看它是不是从应用官网直接下载的。
4. 更新过程中的常见报错与排查链路
镜像源配好了,不代表永远不出问题。下面这几类报错是高频中的高频,我按排查顺序整理一下。
4.1 git fetch 失败、RPC failed 的处理
典型报错长这样:
text复制fatal: unable to access 'https://github.com/Homebrew/brew/': Failed to connect
RPC failed; curl 56 OpenSSL SSL_read: Connection was reset, errno 54
如果发生在配置镜像源之前,原因基本就是访问官方 GitHub 不畅。如果发生在配置镜像源之后,则要检查变量是不是真的生效了。
一个容易出现的问题:你虽然把 HOMEBREW_BREW_GIT_REMOTE 写进了 ~/.zshrc,但 Homebrew 的下载逻辑里,某些子模块或公式仓库还保留着旧的 remote。这时候用 brew update --verbose 可以看到详细输出,能看到它到底在连哪个地址。
我的排查顺序是固定的:
bash复制env | grep HOMEBREW
brew config | grep -i remote
brew update --verbose
如果 env 里变量有,但 brew config 里没有,说明你改的 shell 配置文件和当前终端会话不对应,或者变量被后面某行配置覆盖了。检查一下 ~/.zshrc 里是否还有其他 export HOMEBREW_* 的重复定义。
还有一个很低级但常见的坑:配置文件里写在 source 其他脚本的命令之前,但后续脚本又把变量覆盖了。解决方法是把所有 HOMEBREW_* 的 export 放在配置文件末尾,确保不被覆盖。
4.2 "Homebrew/homebrew-core 不存在"或目录失效类报错
这类报错信息一般类似:
text复制fatal: not a git repository: /opt/homebrew/Library/Taps/homebrew/homebrew-core
Error: Homebrew/homebrew-core does not exist!
先别急着删目录重装。这个报错的本质是 brew 在预期的路径下找不到 core tap。最常见的原因是 Homebrew 版本升级后,默认行为从"完整 clone core 仓库"切换到了"通过 API 获取公式信息",导致旧版本留下的 homebrew-core 目录结构和新版兼容性出了问题。
我的处理办法是:
bash复制brew doctor
brew update-reset
brew update-reset 会把 Homebrew 主仓库和相关 tap 强制重置到远端状态,注意这个命令会丢掉本地的未提交修改。如果你没改过 brew 自带的仓库内容,执行它是安全的。
还有一种更偏僻的情况,我也见过几次:有人把整个 Homebrew 安装到自定义目录,比如 ~/.mybrew,然后某些脚本又把环境变量 HOMEBREW_PREFIX 或 HOME 指到了另一个位置,导致 brew 去查找 /storage/users/xxx/.harmonybrew 这类奇怪路径,然后报 homebrew does not exist。这已经不是镜像源问题了,而是环境变量错位。先检查:
bash复制echo $HOME
echo $HOMEBREW_PREFIX
which brew
确保这三个位置的关联是合理的。如果 HOME 被某些容器或工具链改掉了,Homebrew 的查找路径就会跟着漂移。
4.3 Cache 目录异常与清理
更新和安装过程中下载的临时文件都放在 cache 目录。macOS 上一般是:
text复制~/Library/Caches/Homebrew
如果这个目录里积累了太多损坏的半成品文件,也可能导致更新失败。常见现象是 brew install 下载到一半就报 checksum mismatch,或卡在 Already downloaded 但实际文件不完整。
手动清理时,建议先用 Homebrew 自带的清理命令:
bash复制brew cleanup -s --prune=all
-s 表示清理旧版本缓存,--prune=all 会把所有过期下载缓存清掉。如果问题很顽固,也可以手动查看 cache 目录,但不要整个目录一下子删光,否则刚下好的 bottle 缓存也没了。先看看有没有文件大小为 0 或者明显不完整的文件,有针对地删。
4.4 镜像源配置了但更新还是慢的排查顺序
如果镜像源已配置但更新还是慢,按这个顺序排查:
- 先看
brew config确认所有镜像变量都已生效,尤其是HOMEBREW_API_DOMAIN。 - 再看 DNS 解析,
nslookup mirrors.tuna.tsinghua.edu.cn,看解析到的是不是你期望的 IP。 - 然后用
curl -I测试镜像域名连通性,比如curl -I https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/。如果镜像源本身响应慢,那就是镜像源节点的问题,换一个源。 - 最后才是看 Homebrew 进程是否被某个后台任务占住,比如之前有
brew update卡死没结束,新的更新请求会一直等锁。
很多时候卡住不是配置问题,而是上次更新进程没有退出。打开活动监视器,或者执行:
bash复制ps aux | grep -i "brew update"
如果看到残留进程,先结束它,再把 cache 里的锁文件清理掉,然后再更新。这比反复重装 Homebrew 有效得多。
5. 更新后的清理、回滚与版本管理
镜像源搞定、更新也跑通之后,接下来是很多新手容易忽略的收尾工作。Homebrew 的更新能力强,但长期使用下来也会堆积一堆无用的旧版本。及时清理不光是为了省磁盘,更是为了减少下次更新时可能出现的依赖冲突。
5.1 清理旧版本和不必要依赖
更新完以后先看一眼还能不能正常使用,然后再清理:
bash复制brew cleanup --prune=all
brew autoremove
brew cleanup 会删除旧版本和过期的下载缓存,brew autoremove 会自动移除那些已经没有任何软件依赖它的库。很多开发机磁盘不够大的问题,执行完这两个命令立竿见影。
如果你发现某个软件升级后你不喜欢新版本,Homebrew 不像系统应用商店那样能直接一键回滚。比较实用的做法是在升级前先看当前版本:
bash复制brew list --versions
然后把不想升级的版本钉住:
bash复制brew pin mysql
brew pin 之后,brew upgrade 会跳过这个包,直到你手动 brew unpin mysql。这个机制特别适合数据库、运行时这类不允许随便大版本升级的工具。
5.2 遇到新版本不兼容如何回滚
假设你没有提前 pin,某个包升级后坏了,最常见的方法是:
bash复制brew uninstall <formula>
brew install <formula>@<version>
比如想装旧版 PostgreSQL:
bash复制brew uninstall postgresql
brew install postgresql@14
不过要注意,不是所有 formula 都提供 @版本号 这种安装方式。如果某个包没有这种历史版本安装入口,那就只能从 Homebrew 仓库的历史 commit 里手动切回去,过程比较麻烦,且依赖管理容易出问题。我的建议是:重要工具升级前,先看一眼 brew info 里有没有相关注意事项;如果软件本身还在快速迭代期,不要盲目追新。
日常升级的节奏我也分享一下。我一般每周执行一次:
bash复制brew update && brew outdated
先看看有哪些包落后,再决定要不要升级。如果输出里有一个软件刚发了大版本,但我当前项目还在依赖旧版本,就把那个包 pin 住,其他包正常升级。这个习惯帮我避免了很多"升级一时爽,代码火葬场"的尴尬。
5.3 把更新动作固定成日常节奏
有些人喜欢一上来就 brew upgrade 全部更新,这种方式不是不行,但要接受一个大前提:Homebrew 的 formula 是社区维护的,更新速度和质量参差不齐。今天更新后某个工具突然就崩了,真不一定是你的问题,很可能是 formula 的依赖没写清楚。
所以我的做法是把更新拆分:
- 每周执行
brew update && brew outdated。 - 看到需要更新的包后,先
brew info确认是否影响当前环境。 - 分批次升级,比如先升级 CLI 工具,再升级 Cask 图形应用。
brew upgrade之后立刻跑一遍日常会用到的命令,确认没有明显异常。
这套流程听起来保守,但长期来看最省时间。更新本来就是维护工作,不是追求最新版本刺激。
6. 镜像源要留多久?切换与还原经验
镜像源配置不是一劳永逸的。很多人配完清华源之后一用就是一年,但某天突然发现自己想要安装的一个新软件,镜像源同步延迟导致一直找不到。这时候就要考虑临时切换回官方源试试。
6.1 什么时候建议切回官方源
- 镜像源同步有延迟,官方已经有的新 formula 镜像源还没拉取。
- 你下载的 bottle 在镜像源上校验失败,但官方源能正常安装。
- 你的网络环境本身访问官方源已经没问题了,没必要继续绕一圈。
- 项目 CI 里对依赖的哈希校验非常严格,镜像源偶发的同步问题会影响构建稳定性。
切换之前不一定要删掉 profile 里的配置,可以临时在当前终端取消环境变量:
bash复制unset HOMEBREW_BREW_GIT_REMOTE
unset HOMEBREW_CORE_GIT_REMOTE
unset HOMEBREW_BOTTLE_DOMAIN
unset HOMEBREW_API_DOMAIN
unset HOMEBREW_INSTALL_FROM_API
然后再执行 brew update。如果官方源在这个网络环境下也能正常用,那说明镜像源可以暂时退场;如果一取消就卡死,那就继续用镜像源。
6.2 切换源的注意点
切换镜像源和还原官方源的时候,最忌讳的是只改一个变量。比如你把 HOMEBREW_BOTTLE_DOMAIN 还原成了默认,但 HOMEBREW_API_DOMAIN 还留在镜像源,这会造成 formula 列表从镜像源拿,实际下载安装包却从官方源拿。如果两边同步状态不一致,容易出现安装了旧版本或者 checksum 对不上的问题。
我习惯在 profile 里把配置写成一段带注释的区块,比如:
bash复制# Homebrew mirrors
# export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git"
# export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git"
# export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"
# export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api"
# export HOMEBREW_INSTALL_FROM_API=1
需要切换时,把注释解开或者删掉,再开一个新的终端会话。这样能保证变量要么整套生效,要么整套不生效,不会出现只改一半的问题。
6.3 我的个人配置模板
如果让我给一个最终推荐模板,我倾向清华源为主、中科大源做应急备份。日常使用清华源,遇到清华源抽风就把变量里的域名整体替换为中科大,再开新终端测试。不要在同一套 profile 里同时启用两家,会很大概率遇到仓库哈希值错乱。
另外,我会在终端里额外定义一个简单的别名,用来快速查看当前 Homebrew 走了哪个源:
bash复制alias brewmirror='brew config | grep -E "HOMEBREW_(BREW|CORE|BOTTLE|API)_DOMAIN"'
执行 brewmirror,一眼就能确认当前环境变量。这个习惯帮助我很多次,因为有时候问题根本不是网络,而是某个脚本或某次手动 export 把变量覆盖了。
最后再分享一个小技巧:如果你经常在多个网络环境之间切换,比如公司网络、家里网络、公共网络,不要反复改全局 profile。更好的做法是写两个函数,一个启用镜像源,一个还原官方源,放在 ~/.zshrc 里。需要哪个就调用哪个,避免每次手动注释。镜像源这件事,配置起来不难,难的是理解它到底作用在哪一层。当你真正搞懂了 brew 更新时哪些请求走 git、哪些走 API、哪些走下崽,后续再遇到问题就不会慌了。
