MinIO 把 Docker Hub 仓库删了——但你的对象存储不必陪葬
我先把一个日期钉在这儿:2026 年 9 月 11 日,UTC 时间 18:31 到 19:36 之间,MinIO 把 Docker Hub 上的 minio/minio 和 minio/mc 两个仓库,直接删了。
不是限流,不是换个 tag,是整个仓库返回 404——“repository does not exist”。
那天全世界的 CI 都在悄悄变红。我随手翻了翻 GitHub,各种项目当天就炸出了 issue:docker compose up 起不来,
报的是 pull access denied for minio/minio, repository does not exist or may require 'docker login'。很多人第一反应是自己登录失效了,翻来覆去查凭证——其实跟凭证一点关系没有:
Docker Hub 对“不存在的仓库”和“需要登录的私有仓库”返回的是同一句话,匿名用户根本分不清。有人查了半天才反应过来:不是我坏了,是上游没了。
有个荷兰开发者在自己项目的 issue 里写得最干脆,我原样翻译过来:
五、六月 MinIO 把免费社区版的整个管理控制台砍成了残废;十月停止发布社区版的镜像和二进制,仓库挂上“不再维护”;
今年二月仓库正式归档;9 月 11 日,minio/minio 和 minio/mc 被彻底从 Docker Hub 抹掉——不是限流,就是没了。
我是老冯,Pigsty 的作者。Silo,就是我维护的那个 MinIO 社区 fork。
这篇不是 release notes。Release notes 我另外写了,一条一条列变更,那是给要升级的人对着看的。这篇是篇总结——讲讲过去这一个多月到底发生了什么, 为什么我要在两个前沿模型刚发布的节骨眼上,带着两个 AI 干了一场“漏洞大清理”,以及,为什么我觉得这事儿值。
MinIO 是怎么一步步跑路的
要讲清楚 Silo 为什么存在,得先把 MinIO 这几年怎么把自己活活作死的时间线摆出来。这不是情绪,是有据可查的一串日期:
2021 年,MinIO 把核心服务器代码从宽松的 Apache 2.0 换成了限制性很强的 AGPLv3 (其实2019 年就开始铺垫)。当时社区就吵过一轮, 但大多数用户无所谓——反正我自己用、不对外提供服务,AGPL 和 Apache 对我没差别。真正的问题埋在后面。
2025 年五六月,MinIO 干了让社区最愤怒的一件事:把免费社区版的完整管理 Console 砍了,只留一个残缺的对象浏览器。用户管理、桶策略、访问控制、生命周期管理——一夜之间全没了。想要回来?行,上企业版,一年大概十万美元起步。
最讽刺的是,我后来把 Console 恢复回来,根本不需要逆向。他们就是把 minio/console 的子模块版本往前一退、换了个依赖版本,
把完整 Console 换成了阉割版。我把那个依赖版本退回去,Console 就回来了。
2025 年 10 月,MinIO 停止为社区版发布预编译二进制和容器镜像,改成“只给源码”。你想用?自己编译去。
2025 年 12 月 3 日,MinIO 在 GitHub 上宣布社区版进入“维护模式”。README 说安全修复会“逐个评估”。我当时就写了篇《MinIO 已死》开喷。
2026 年 2 月 12 日,仓库状态从“维护模式”改成“不再维护”,然后正式归档,只读。一个 6 万 star、十亿+ Docker 拉取的项目,变成了一块数字墓碑。而就在那个归档的仓库里,SECURITY.md 还写着一行字:“我们永远为最新版本提供安全更新。”
2026 年 9 月 11 日,删 Docker Hub 仓库。这是跑路的最后一步,也是最狠的一步——前面几步好歹还给你留了个念想:镜像虽然旧,但还能拉。这一步是连念想都没了。
这条线连起来看,是一个非常干净的商业设计:归档仓库,这样就没有修补的义务;继续发 CVE 公告,保持“曝光度”;然后把所有读到公告的人,一股脑引导到商业版 AIStor。 AIStor 按容量收费,$0.02/GB/月,400 TiB 以内适用企业自助支持层——据第三方评测,起步价大概九万六千美元一年。对,就是这么个“清爽”的安排。
那总得有人来修那个被抛弃的老版本吧。这个人就是我。
为什么是我,为什么用 AI
其实早在 2025 年 12 月 MinIO 宣布维护模式的时候,我就已经自己编好了打过 CVE 补丁的二进制,切过去用了。因为 Pigsty 自己在生产里用 MinIO 做 PostgreSQL 的备份仓库——这不是“我想做个开源项目”,是“我依赖的东西坏了,我得修”。
一开始我很偷懒。点一下 Fork,把包编出来,名字都懒得改,仓库就叫 pgsty/minio。我一个人用的时候这没问题。但等到镜像拉取超过五十万次、一堆知名开源项目开始把默认镜像指向我这儿的时候,这就不行了——MinIO® 是注册商标,开源许可证不授予商标权,我不能把这个隐患甩进别人的供应链。所以 8 月我做了件事:把项目、仓库、二进制、所有面向品牌的表面,从 MinIO 全部重命名成 PGSTY SILO,简称 Silo。在数据圈,silo(数据孤岛)通常是个骂人的词——我偏要拿它当名字。谷仓 silo 摆在 Pigsty 猪圈旁边,再加上 pig 包管理器和 sow 仓库工具,一家人整整齐齐。
那么,一个人怎么维护一个中等规模的 Go 代码库、还要处理不断掉下来的高危 CVE?答案是 AI。但绝不是对着 AI 说一句“去把所有问题都解决掉”那么简单。
我在 4 月那篇《续命 MinIO:承诺兑现》里写过当时的流程:Codex 起草,Claude Code 做对抗式 review。到了这一轮 8、9 月的大清理,模式升级了, 但内核没变——两个异构的 AI Agent 相互对抗,我在上面做裁判。
具体到这一个多月:9 月 1 日 Anthropic 发了 Claude Fable 5.1,9 月 3 日 OpenAI 发了 GPT-6 Astra。两个当时的顶级模型前后脚落地, 这是我决定动手做一轮大清理的直接契机——工具够硬了,很多以前“知道有问题但改起来风险太大”的深水区,现在可以碰了。
分工是这样的:Astra 6 发布之前,大体是 Fable 操刀设计、Sol(GPT-5.6 Sol,通过 Codex 用)负责实现;Astra 6 发布之后,基本由 Astra 自己负责设计和实现,
Fable 转过来做核对和对抗性 review。 你去翻 Silo 的 commit 记录能看到痕迹:PR 的 co-author 里挂着 Claude Opus 4.8、Claude Fable 5.1,PR 描述里有 “Codex Astra 6 (Max) adversarial review: VERDICT PASS” 这样的字样,
分支名带 codex/ 前缀——比如修 SN-2026-011 那个未签名头漏洞的分支就叫 codex/unsigned-amz-header-copy-20260909。每一个 AI 提交,
最后都带一行 Signed-off-by: Feng Ruohang。 那是我。
为什么非要两个 Agent 互掐?因为我发现,单个 Agent 修安全漏洞,特别容易给你一个“听起来很有道理、但悄悄漏了一个边界条件”的补丁。它太自信了。让第二个不同厂商的模型站在攻击者的角度专门挑它的刺,绝大多数这种“自信的漏洞”就被筛出来了。而且对抗会逼着它们把权衡写下来——当两个实现分叉的时候, 总得有一方说清楚“为什么选 A 不选 B”。这段争论,恰恰是我作为最终拍板的人唯一能真正拿来判断的东西。如果它们俩一拍即合、都不解释,那我反而慌。
我的角色,是宏观把控、分歧调解、资源协调。我不逐行看实现细节——那样我一个人根本扛不住。我定义问题、设约束、在两个方案里挑、读 diff、跑测试、决定合并还是打回去。真正的维护从来不是“一击必杀”,而是“补丁摞补丁”。LDAP STS 那个限流修复就是典型:第一版落地后我们才意识到,成功的请求不该计入限流、X-Forwarded-For 默认不能信、限流键得用“源 IP + 归一化用户名”而不是单一维度——前后三个跟进 commit 才算稳。这种活儿要是纯手工一轮轮磨,时间成本高得离谱。
【待补:这一轮大清理里,有一次 Astra 和 Fable 在某个具体修复上僵住、我出面调解的真实细节,可在此补一个有画面感的小故事。】
这一个多月,到底修了什么
好,讲点硬的。
从 8 月初到 9 月 17 日,我发了四个服务器版本:08-04、08-06、09-03、09-16。光是最后的 09-16 版, 单次就吞了 210 个提交(其中 140 个非合并提交)。
安全这块,把 4 月以来的账一起算:8 个 CVE + 十来个 SN 项目本地条目,官网口径是“14 个安全修复、17 条安全说明”。这些全部继承自已归档的上游 MinIO——也就是说, 这些洞在 MinIO 社区版里永远不会被修,官方只会让你去买 AIStor。挑几个说说它们有多离谱:
- CVE-2026-33322,OIDC JWT 算法混淆,CVSS 9.8。 在某些身份提供商配置下,一个知道 OIDC ClientSecret 的攻击者,
可以伪造一个声称自己是任意身份——包括
consoleAdmin超管——的令牌,而 MinIO 会照单全收。这个洞的脆弱窗口从 2022 年 11 月一直开到 2026 年 3 月,大约三年半。 - CVE-2026-34204,复制头元数据注入。 一个普通的 PUT 或 COPY 请求,带上特定的
X-Minio-Replication-*头, 就能把一个对象写成永久不可读的状态。数据还在盘上,你就是读不出来。 - 两个签名验证绕过(CVE-2026-40344 / CVE-2026-41145)。 unsigned-trailer 路径上的匿名或伪造签名请求,能在某些路由上成功写对象。这俩只有 GHSA 编号、没有公开的 CVSS 数字,但性质是 critical 级的。
- CVE-2026-42600,ReadMultiple 存储层路径穿越。 这个更值得一说——我们一开始只是删了那个没人用的内部端点,就宣布“修好了”。后来做审计才发现,端点是删了,根本病因还在另外三个协议面上活着:请求体和 grid 帧从不经过校验中间件,存储层也没有围栏。于是有了 SN-2026-002,把剩下的账还清:路径和卷两个维度的穿越、一个每帧就能弄崩一个节点的除零 panic、把截断分片误报成完整的元数据、三处按调用方声明值分配的内存……12 个缺陷,全是从上游继承的,我们的 diff 在受影响文件上是纯删除、零新增行。 这个教训我专门写了篇复盘:修一个端点,不等于关掉一整类漏洞;当你决定把下面那层先不管,一定要写在下一个人会绊到的地方。
9 月这轮还落地了一批更硬核的:持久化 IAM 吊销(保留删除修订和父级吊销边界,让旧凭证没法越过吊销历史复活)、多池对象一致性(条件 PUT、分片完成、条件 DELETE 在共享命名空间锁下评估同一个逻辑对象)、加密与联邦复制的一整套修复(原样保存 SSE-C 密文副本、保留分片明文长度、正确处理 Object Lock 头)。
而且有几个洞是用户报给我们的,这点我要特别点名感谢:SN-2026-011 那个未签名 x-amz-* 头的问题,
是 Oren Yomtov(@orenyomtov)私下报告的——防的是一个签名 PUT 被偷换成带签名者权限的 CopyObject;Console 匿名分享代理的问题是 jiri-pejchal 报的;
慢 HTTP 拒绝服务是 @AEGEGE(issue #183)报的;CPU 指标并发读崩溃是 @aschyolkin(issue #210)报的。
除了安全,还有一大堆上游遗留的非安全缺陷被顺手清了,很多是兼容性和默认行为的权衡,这里能看出我做这事儿的分寸:
- 客户端来源地址可信性,我做成了需要显式开启的加固。上游那套
X-Forwarded-For/X-Real-IP/ RFC 7239Forwarded三个头都能被伪造的问题确实存在, 但我新增了MINIO_API_TRUSTED_PROXIES这个显式信任配置——不设置就完全保持上游行为。为什么?一旦默认改了,所有在反向代理后面跑的存量部署都可能被打断。这种加固,我宁可让你主动打开,也不替你做决定。 - distroless 镜像变体:单文件、128MB(经典镜像是 199MB),内置 HEALTHCHECK,
/data层内可写。但我没有直接去瘦身经典镜像——经典镜像的内容本身是一个兼容性表面, 之前上游就是在这个表面下偷偷改东西把大家坑过。所以 distroless 是一个新名字、新契约,谁的现有 healthcheck、docker exec mc习惯都不会被打断。 - 原生
silo healthcheck子命令:容器里不再需要 shell、curl 或 mc 来做探针。 - 还有
mc watch、桶通知、S3 Select keep-alive 的流式刷新回归修复,逐桶 CORS 落地,桶元数据串行化(一把metadata.lock防止并发写互相覆盖),缺桶时正确返回NoSuchBucket……
还有一个我想强调的取舍:外部贡献者的 PR,我不是照单全收。 比如那个 CPU 指标竞态的修复,@mrjavadseydi 提了 PR #215,锁改得是对的、回归测试也能复现问题,但我已经在主线 #214 里用自研方案实现了同样的修复,就把他的 PR 作为 superseded 关闭了——同时在贡献者致谢里认了他的功劳。关掉一个 PR 不等于否定贡献; 采用哪个方案,是维护者要负责的技术判断。 这一个多月,一共有 50 位社区贡献者(49 位人类 issue/PR 作者 + 单独致谢的安全报告者 Oren Yomtov)参与进来, 外部合并的代码 PR 涵盖 go-jose/OpenTelemetry 依赖 CVE、桶通知流、逐桶 CORS、联邦 CopyObject legalhold、multipart ChecksumType 等等。这早就不是我一个人的项目了。
兼容性方面,官方 compatibility 页面把 Silo 相对 MinIO 的差异梳理成 12 项兼容性改进、4 项细微差异、8 项条件性检查,共 24 类,
比较基线是上游 2025-12-03 那版源码。我得诚实地补一句:这 24 类是按用户场景分的,不是兼容率百分比——我不想给你一个“99.x% 兼容”的漂亮数字然后让你踩坑。能给的硬证据是内部验收:make verify 覆盖 FS、纠删码、分布式纠删码、多池、IPv6 多池,174 PASS / 0 FAIL;四节点 TLS 集群做过 1004 对对象的上传下载校验和逐一对比、双站点复制、单盘重建、完整回滚演练。
这事儿到底花了多少,值不值
说实话,花了不少。
这绝不是对 AI 喊一句“go and fetch 所有问题去解决”就行的事。它涉及大量的利弊权衡:哪些洞现在修、哪些留着;默认行为要不要改、改了会不会打断存量部署;兼容性的边界画在哪里; 一个外部 PR 是收编还是自研替换。这些判断,AI 给不了我,只能我来拍。
我手上有 15 个 AI 订阅。这一个多月的 Silo 大清理,大概吃掉了其中 3 个 200 美元订阅的全部额度,再加上我相当可观的一段人工时间。 (我在别处的博文里给过一些不同口径的数字——“维持 7 个 200 美元订阅、烧掉一千亿多 token”“某一天烧掉 4 个 20 倍订阅”之类——不同时间段、不同算法, 别较真精确值,量级感受一下就好:一个人 + 一堆顶配 AI 订阅 + 大量夜晚。)
但我觉得这确实是一件很有意义的事。
意义在哪?在于 2026 年,“我来修”这句话的成本变了。放在一两年前,一个人想扛下一个中等规模 Go 代码库的安全维护——跟着 CVE 跑、写补丁、做对抗审计、保住供应链——基本是天方夜谭。现在有了两个能互相拆台的编码 Agent,加上一个愿意在中间当裁判、做取舍的人,这件事变得可行了。这不是什么关于开源韧性的宏大理论,就是当下一个很具体的操作点。
而且它托住了很多真实的部署。Pigsty v4.5 已经把对象存储模块整个切到了 Silo,minio_type 现在只接受 silo 这一个值。外面 RAGFlow 的默认 Compose、Dokploy 的产品模板、Grafana Loki 的 Helm chart、Dell 的 Omnia HPC 平台、nixpkgs、DaoCloud 镜像……几十个项目已经把默认镜像指向了我这儿。这个应急 fork,某种意义上已经成了最活跃、最显眼的那个 MinIO 分支。
如果你还在跑 MinIO
结论很简单:MinIO 跑路了,9 月 11 日删 Docker Hub 仓库是最新、也最决绝的一步。而你可以换成 Silo。
Silo 的定位从第一天起就没变过,就一条规矩:产品和交付表面全部重命名,协议和你的数据一个字不动。 silo 二进制、包、systemd 服务、Helm chart、容器镜像换了名字;
但 S3 和 Admin API、MINIO_* 环境变量、minio_* 指标、x-minio-* 头、/minio/* 路由、.minio.sys 磁盘格式——全部原样保留,而且有 CI 兼容性检查钉死。
这就是我给存量 MinIO 部署的承诺:不动你的数据,持续修漏洞,持续出镜像,保持接口兼容。 官网那句标语我一直很喜欢:
S3 Interface, Libre Object Store. Keep the interface. Own the objects.
保住接口,掌控你自己的数据。
迁移很便宜。Docker 用户把 minio/minio 换成 pgsty/silo 就行,卷不用动、数据不用动、API 不用重学;
容器入口还会翻译遗留的 minio 参数(有人真的这么干了),连 command: minio server /data 都能继续跑。
RPM/DEB 在 GitHub Releases 上有,或者用 pig 装。想要一套现成的 HA 生产部署,Pigsty 免费给你一套。
我用的东西坏了,我在修它。就这么回事。下一个 CVE 掉下来的时候,我还在。
