上一篇 mem0 的文章提到 oMLX 在这台机器上提供对话和嵌入模型,但有一件事没说:这些模型都不在内置硬盘上。整个模型库放在一块外接 SSD 里,另有第二块硬盘每小时对它做一次备份。这篇文章讲这两半是怎么搭的。
为什么用外接 SSD
跑服务的 Mac mini 内置硬盘只有 1 TB,而 MLX 模型的体积增长很快 —— 单个 27B 的 4bit 模型就是 ~15 GB。目前我的模型库已经 36 GB,还在涨:
/Volumes/Mate_Mini/omlx/base/models/
├── Youssofal/Qwen3.8-27B-MTPLX-Optimized-Speed
└── mlx-community/
├── bge-m3-mlx-8bit
├── gemma-4-e2b-it-4bit
└── Kokoro-82M-bf16
从一块通过雷雳(Thunderbolt)连接的 SSD 加载模型,体验和内置硬盘几乎没有差别 —— 权重只会一次性读入统一内存,之后的推理是计算受限的。内置硬盘省下的空间(以及免去反复下载几个 GB 大文件的磨损),远比那点感知不到的加载时间差距值钱。
让 oMLX 指向 SSD
oMLX 把所有东西 —— 模型、HF 缓存、日志、配置、用量统计 —— 都放在一个 base path 之下。迁移只需要改一个设置,应用会把这个选择持久化到一个 bootstrap 文件里:
$ cat ~/Library/Application\ Support/oMLX/base-path
/Volumes/Mate_Mini/omlx/base
通过 Homebrew 安装的 omlx CLI 其实只是一个 shim:读取这个文件、导出为环境变量,然后把控制权交给 App bundle 里的真正二进制:
BOOTSTRAP="$HOME/Library/Application Support/oMLX/base-path"
if [ -r "$BOOTSTRAP" ]; then
IFS= read -r OMLX_BASE_PATH < "$BOOTSTRAP" || OMLX_BASE_PATH=""
if [ -n "$OMLX_BASE_PATH" ]; then
export OMLX_BASE_PATH
fi
fi
exec '/Applications/oMLX.app/Contents/MacOS/omlx-cli' "$@"
base path 里最重要的文件是 settings.json,它显式记录了模型目录:
{
"model": {
"model_dirs": ["/Volumes/Mate_Mini/omlx/base/models"]
}
}
重启服务器(omlx restart)之后就从 SSD 对外服务了。我开了 launch_at_login,台式 mini 上硬盘一直挂着 —— 如果你的使用场景不同,看文末的坑。
为什么要备份"可以重新下载"的文件
模型权重是公开资源,丢了总能重新下。但慢速或按流量计费的网络下,36 GB 意味着几个小时的等待;而且这个目录不只有权重:settings.json、每个模型的调优参数 model_settings.json、hf_download_tasks.json,还有存了服务统计的 usage.sqlite3 —— 这些是下载不回来的。备份的目标不是给 Hugging Face 的目录做灾备,而是让"恢复目录、重启服务"变成一个五分钟的操作。
用 Kopia 做备份
Kopia 是一个去重、加密、内容寻址的备份工具 —— 可以理解为调度更友好的 restic。我在 mini 上跑 KopiaUI,仓库放在第二块外接盘(一块 9 TB 的机械盘)上,因为源和备份在同一块盘上不叫备份,叫副本:
源目录: /Volumes/Mate_Mini/omlx (3 TB SSD)
仓库: /Volumes/Getea/VM-backups/mate_mini_backups (9 TB HDD)
仓库在磁盘上是密码加密的 —— 所有 Kopia 仓库都是如此。这个仓库还存了同一块 SSD 上的另外三个目录(虚拟机、语音模型、容器数据),Kopia 会在它们之间做全局去重。
oMLX 目录的策略,和 UI 里配置的一致:
- 调度: 每小时一次快照
- 保留: 5 个最新、24 个小时级、12 个天级、4 个周级、24 个月级、3 个年级
- 压缩:
zstd-fastest
等价的 CLI 命令(直接用 KopiaUI 内置的二进制):
KOPIA="/Applications/KopiaUI.app/Contents/Resources/server/kopia"
"$KOPIA" policy set /Volumes/Mate_Mini/omlx \
--snapshot-interval=1h \
--keep-latest=5 --keep-hourly=24 --keep-daily=12 \
--keep-weekly=4 --keep-monthly=24 --keep-annual=3 \
--compression=zstd-fastest
模型库是 Kopia 的最佳场景
对模型目录做快照几乎零成本,因为模型权重是不可变的:bge-m3-mlx-8bit 下载完之后,那几个 GB 就再也不会变。Kopia 对所有内容做分块和内容寻址,所以第一次快照之后,每小时的运行几乎不需要上传任何东西。真实的快照列表能看出来:
2026-10-04 07:01 38.4 GB files:1091 (daily-4) ← Qwen3.8-27B 入库
2026-10-04 23:02 38.4 GB files:1099 (hourly-19..24, daily-3, weekly-2)
+ 7 个完全相同的快照,直到 2026-10-05 19:02
2026-10-05 20:02 38.4 GB files:1099 (latest-1..5, hourly-1..18, ...)
+ 17 个完全相同的快照,直到 2026-10-06 13:00
已经攒了 30 个快照,而那些"完全相同"的运行每次只花几 KB 的元数据。十月初从 13.1 GB 跳到 38.4 GB,是那个 27B 模型入库 —— 一次增量上传新数据块,然后恢复平静。这是理想的备份画像:激进的调度、宽裕的保留期、可以忽略的增量成本。
压缩算法值得说一句:4bit 量化的 safetensors 本身已经很致密,任何压缩算法都不会有明显效果。选 zstd-fastest 是给目录里的 JSON/SQLite/文本文件上个便宜的保险,同时不在权重文件上浪费 CPU。
踩过的坑
- 登录时的挂载顺序。 oMLX 开了
launch_at_login。如果服务器启动时 SSD 还没挂载,base path 就解析不了。台式 mini 的盘一直接着,不会遇到;笔记本上的话,要么关掉登录自启,要么接受"没插盘 oMLX 就不工作"。 - KopiaUI 只在运行时才调度。 每小时一次的间隔是由 App 执行的,不是系统守护进程。KopiaUI 常驻菜单栏并随登录启动,所以没问题;一旦退出 App,快照就停了。想要完全无头的方式,就用 launchd 跑
kopia server start。 - 备份必须跨两块物理盘。 源在 SSD,仓库在机械盘。备到同一块盘上的话,盘坏了两份一起没 —— “我有备份"最常见的翻车方式就是这个。
- 快照包含
cache/和logs/。 可以在源目录放一个.kopiaignore把它们排除掉。这个规模下我懒得弄 —— 去重让这些噪音零成本,而且全局策略里的ignore_cache_directories已经会跳过系统标记的缓存目录。 - 对运行中的目录做快照是安全的。 oMLX 服务过程中会写
usage.sqlite3和日志,但权重文件是静态的。最坏情况是快照里的统计数据库稍微滞后,任何恢复场景都不在乎这个。
验证它是否工作
KOPIA="/Applications/KopiaUI.app/Contents/Resources/server/kopia"
"$KOPIA" snapshot list /Volumes/Mate_Mini/omlx \
--config-file ~/Library/Application\ Support/kopia/repository.config
而真正重要的验证是恢复演练:挂载一个快照(kopia mount),或者 kopia snapshot restore <id> /tmp/omlx-test,然后把 oMLX 的 base path 指到恢复出来的目录,确认模型能正常加载。没恢复过的备份只是一个假设。