Ollama 模型占 C 盘:移到 D 盘实测与四个坑

能迁,全程约十分钟,桌面版必须完整退出再启动才生效。实测在 Ollama 0.40.14 上,复制目录加一个环境变量就够,模型不用重下。真正危险的动作是复制到一半就把旧目录删掉,那种情况下 ollama list 会干净地列出所有模型,一发消息却报模型不存在。

这篇写给 C 盘被模型吃掉几十 GB 的人,也适用于想把模型库放进外置 SSD 的 macOS 用户。所有实测数字来自一台 Apple M4 / 16GB 机器,系统 macOS,Ollama 0.40.14,模型库 23.28GB。目录机制在三个平台一致,Windows 专属的差异我单独标出来。

先确认它在读哪个目录,别急着搬

模型目录不是凭猜的。当前运行的 serve 进程环境里就写着答案,在 macOS 或 Linux 上一条命令能打出来:

ps eww -p $(pgrep -f 'ollama serve' | head -1) | tr ' ' '\n' | grep OLLAMA_MODELS

本机实测输出是一行 OLLAMA_MODELS=/Users/yc/.ollama/models。这里出现了一个值得注意的现象:这个值由桌面应用在拉起 serve 时自己写进进程环境,也就是说变量在应用启动那一刻就被定下来了。

更省事的通道是日志。serve 启动时的第一行结构日志会把全部环境变量摊开,找 msg="server config" 那行:

time=2026-11-10T05:46:19.756+08:00 level=INFO source=routes.go:2474 msg="server config"
env="map[... OLLAMA_MODELS:/tmp/olm1 OLLAMA_HOST:http://127.0.0.1:11510 ...]"

日志在 ~/.ollama/logs/server.log。Windows 上的桌面版把同一批文件放在 %LOCALAPPDATA%\Ollama\ 下,server.log 与 db.sqlite 都在那里。想验证变量到底有没有被读到,看这一行比看任何图形界面的设置页都准。

迁移四步,每步都有验证动作

第一步 停干净

托盘右键退出只是关了窗口。Windows 上加一次确认:

tasklist | findstr /i ollama

输出为空才算停完。macOS 用 pgrep -fl ollama,看到 ollama serve 就说明还活着。进程没退就复制,正在写的 blob 会被读成半个文件,落进坑一。

第二步 复制,不要剪切

Windows 用 robocopy,比资源管理器稳,还能续传:

robocopy "%USERPROFILE%\.ollama\models" "D:\OllamaModels" /E /COPY:DAT /R:1 /W:1

这里有个容易被误判的地方:robocopy 的返回码 0 到 7 都表示成功,8 以上才是出错。很多批处理脚本用 if errorlevel 1 判断,会把成功当失败,然后中断流程。macOS 或 Linux 换 rsync:

rsync -a --info=progress2 ~/.ollama/models/ /Volumes/SSD/OllamaModels/

第三步 设变量,指向 models 本身

Windows 建用户级变量就够了,不需要管理员权限:

setx OLLAMA_MODELS "D:\OllamaModels"

注意路径的层级。OLLAMA_MODELS 要指向 models 目录本身,父目录不行。写成 D:\ollama 而文件实际躺在 D:\ollama\models,结果就是列表空白,而且日志里看不出错。macOS 或 Linux 写进 shell 配置文件:

echo 'export OLLAMA_MODELS=/Volumes/SSD/OllamaModels' >> ~/.zshrc

第四步 重启并做三次验证

setx 只对新开的进程生效,当前终端里的值不会变,所以验证必须新开一个窗口。第一步看数量:

ollama list

条数要和迁移前一致。第二步做真正有效的检查,show 会去磁盘读权重文件的头部:

ollama show qwen3:8b

能打出参数、模板、量化类型,说明 blob 真的到位了。第三步跑一次推理,随便一句 hello 即可。list 只读 manifest,show 才碰 blob,这两个命令的差别是后面排错的关键。

坑一:列表正常,一问就说不存在

这是迁移失败最常见的形态,也是最具欺骗性的一个。实测把 manifest 里体积最大的那个 layer 对应的 blob 删掉,模拟复制中断,然后冷启动服务发一次请求,返回:

HTTP/1.1 404
{"error":"model \"all-minilm\" not found, try pulling it first"}

同时 ollama list 一切正常,还完整显示 45 MB 的体积,因为这个数字是从 manifest 里的 size 字段加出来的,跟磁盘上有没有文件无关。如果只看 list,会误判为迁移成功。

真正的定位命令是 show,它给出的报错带着绝对路径:

Error: read GGUF metadata "/tmp/olm4/blobs/sha256-797b70c4edf8...": open
/tmp/olm4/blobs/sha256-797b70c4edf8...: no such file or directory

看到 no such file or directory 加一个 blobs 路径,就能确定是文件缺了,跟模型名写错、跟版本不兼容都没关系。修复只需要重新拉一次同名模型,已经存在的 blob 按 sha256 跳过,只补缺的那一层。想主动确认某个模型完不完整,跑一遍 show 即可,比看目录大小可靠。

同样的文件级症状在 ComfyUI 上是另一套报错,模型放错子目录会直接体现在加载器的下拉框里,那一路的排查写在 safetensors 损坏与下拉框空白这篇里,两套引擎的文件校验思路可以互相参照。

坑二:变量设了,一点反应没有

三个原因按出现频率排。排在第一位的是进程比变量活得久。变量在进程启动时读一次,之后改系统设置对它毫无影响。本机实测桌面版 serve 进程的环境里 OLLAMA_MODELS 已经被应用自己写成默认路径,终端里 export 一个新值改不动这个进程。必须从托盘完整退出,再重新启动应用。

第二个是用户变量与服务身份不匹配。把 Ollama 注册成系统服务运行时,进程以 SYSTEM 身份启动,读的是系统级变量,当前用户设的用户变量它看不见。这种情况要把变量加到系统变量里,或者用软链接方案绕开。

第三个是路径多套了一层,前面第三步已经说过。这三种情况的共同点是界面不报错、日志不报错,只有 msg="server config" 那行的值能说明问题。养成迁移前先存一份那一行日志的习惯,出问题时对比一眼就清楚。

坑三:网上教的改 db.sqlite,现在没用了

英文社区流传一个说法,说桌面版会静默忽略环境变量,要手动改 db.sqlite 里的 settings 表才能改路径。这个说法在早期版本可能成立,在当前版本已经不成立。我把本机的库文件拷出来直接看了结构,settings 表一共 26 个字段,schema_version 是 19,字段清单里 models 存的是模型名录文本,context_length 是上下文长度,working_dir 是工作目录,没有任何一个字段叫模型路径。

sqlite3 db.sqlite ".schema settings"

表里没有这个字段,改它当然不会搬目录。况且该表主键被约束为 id=1,手工写坏之后应用可能起不来,风险远大于收益。这条路可以划掉。

坑四:只复制了一半的目录

models 目录下面现在有四个子目录:blobs、manifests、metadata、manifests-v2。看着像要全搬,其实只需要两个。实测只复制 blobs 和 manifests,用新目录冷启动,list 正常,embedding 请求也正常返回向量,metadata 与 manifests-v2 在第一次加载时自动重建。

反过来漏掉 manifests 就麻烦了。那个目录只有 20KB,装的是每个模型的清单文件,指向 blob 的 sha256。少了它,磁盘上是十几个 GB 的无名文件,list 一片空白,看起来像模型全丢。复制前先记下 manifests 的三个层级:manifests/registry.ollama.ai/library/<模型名>/<tag>。

磁盘账:换盘要留多少空间

实测这台机器的模型库由 5 个模型、16 个 blob 文件构成,按 manifest 里的 size 字段累加是 23.28GB,和 du 出来的磁盘占用完全一致。单个模型的账目如下。

模型 体积 层数
qwen3.5:9b 6.59GB 3
qwen3-vl:8b 6.14GB 4
qwen3:8b 5.23GB 4
qwen3-embedding:8b 4.68GB 1
qwen3-embedding:0.6b 0.64GB 1
合计 23.28GB 16 个 blob

想自己算一遍,manifest 是纯 JSON,读 layers 里的 size 求和就行:

cd ~/.ollama/models
python3 -c "
import json,glob
s=0
for m in glob.glob('manifests/registry.ollama.ai/library/*/*'):
    d=json.load(open(m))
    s+=sum(l['size'] for l in d['layers'])
print(round(s/1e9,2),'GB')"

换盘规划按两倍留。拉取模型时下载的临时文件与最终文件会短暂共存,6GB 的模型至少要 12GB 余量。另外一条能省心的事实:同一份权重被多个模型引用时不重复占空间。实测用 FROM all-minilm:latest 派生一个新模型,blob 文件从 5 个变成 8 个,目录总大小 44MB 一动不动。自己用 Modelfile 调的参数版本几乎不花钱,所以别为了省空间去删这些派生模型。

磁盘空间拿回来了,内存不一定跟着放。被加载过的模型会按 keep_alive 的窗口继续占着显存,窗口怎么设、四种写法有什么区别,见 keep_alive 四写法实测。

不想动环境变量:软链接方案

把默认目录换成一个指向新盘的链接,效果等价,且对服务身份不敏感。macOS 或 Linux 上实测通过,list 与 embedding 请求都正常:

mv ~/.ollama/models ~/.ollama/models.bak
ln -s /Volumes/SSD/OllamaModels ~/.ollama/models

Windows 用目录联接,比符号链接稳,不需要开发者模式也不需要管理员:

mklink /J "%USERPROFILE%\.ollama\models" "D:\OllamaModels"

风险在于应用升级或卸载时可能重建默认目录,链接被覆盖后模型就看不见了。所以这条路适合已经稳定运行的机器,不建议装机第一天就用。

三种做法怎么选

做法 生效范围 要重启应用 实测结论
环境变量 OLLAMA_MODELS 该进程拉起的 serve 要,托盘完整退出 有效,首选
软链接 / 目录联接 全平台一致,不依赖变量 要 有效,怕升级覆盖
改 db.sqlite 的 settings 表 无 不用试 当前版本无该字段,无效

Windows 上如果模型库超过 100GB,建议选环境变量,理由是可回退:变量删掉就回到默认目录,文件还在原地。软链接一旦被升级覆盖,排查成本高得多。

迁完的收尾清单

旧目录不要立刻删。先跑 ollama list 对数量,再挑最大的那个模型做一次 ollama show 和一次真实推理,两条都过再删。服务启动日志里有一行 total unused blobs removed,它会告诉你自动清掉了多少没被引用的残留文件,迁移后第一次启动值得看一眼,数字异常大说明有半截文件被留在新目录里。

顺手可以对照一下显存与上下文的其他配置项,模型放对地方只是第一步。显存怎么按上下文长度估算有完整公式,见 本地大模型显存怎么算;上下文该给多大、为什么 4096 实际只吃 2050 token,见 num_ctx 实测。这两项跟目录无关,但同样会在换机或换盘之后重新算一遍。

常见问题

迁移后需要重新下载模型吗

不需要。模型文件按内容哈希命名,manifest 里记着每个 blob 的 sha256,只要文件在对应位置,Ollama 就能认出来。只有文件缺失时才会补下载,而且只补缺的那一层。复制完成到验证通过之前,旧目录务必保留,这是唯一的回退路径。

能把模型放在 NAS 或网络盘上吗

技术上可以挂载,实际不建议。每次加载模型都要把整个权重读进内存,6GB 的模型走千兆网络光读盘就要一分钟以上,而本地磁盘上实测冷启动只要 4.28 秒,其中读盘占 3.86 秒。网络盘的延迟会直接乘在这个环节上,日常对话的首字延迟会变得难以忍受。

只想腾空间,不想迁移,删什么安全

唯一安全的方式是 ollama rm 模型名,它会同时清掉 manifest 和不再被引用的 blob。直接删 blobs 目录里的文件会造成列表正常、一跑就 404 的局面,也就是坑一。如果已经手删过,两条路:重新 pull 同名模型补齐,或者删掉对应 manifest 让它从列表里消失。