在飞牛私有云fnOS上本地部署DeepSeek-R1:8B模型实操指南

1. 项目概述:在飞牛私有云上跑起 DeepSeek-R1,不是“装个模型”那么简单

你手头有一台已经刷好fnOS 飞牛私有云系统的 NAS 设备——它不是普通路由器盒子,也不是套壳群晖的玩具,而是一套基于 Debian 12、深度定制、自带 Web 管理界面、预装 Docker 和 systemd 服务管理能力的轻量级私有云操作系统。它的硬件通常搭载 Intel N100/N5105 或 AMD Ryzen R1606G 这类低功耗 x86 平台,内存多为 8GB~16GB,自带 M.2 NVMe 插槽和双千兆网口。这类设备的真正价值,从来不是“存文件”,而是作为家庭/工作室边缘 AI 计算节点的物理基座。

而你真正想干的事,是让这台 NAS本地运行 DeepSeek-R1 模型,并能从家里局域网任意设备(手机、笔记本、平板)甚至外网(比如下班路上用手机访问)调用它——不是调用 API,是实打实的本地推理;不是跑个 0.5B 小模型凑数,是真正把DeepSeek-R1:8B(注意,不是 R1-7B,也不是 R1-14B,是官方发布的 8B 参数版本)稳稳地 load 进显存或内存,完成代码生成、数学推理、长文本理解等真实任务。这不是 Docker 跑个 hello-world,它牵扯到 fnOS 的内核参数调整、Docker 容器资源隔离策略、Ollama 的模型加载机制、GPU 加速路径选择(如果你的 N100 支持 OpenCL/Vulkan 推理)、模型量化格式选型(Q4_K_M 还是 Q5_K_S?)、反向代理配置逻辑,以及最关键的——如何绕过 Ollama 默认只监听 127.0.0.1 的安全限制,又不破坏 fnOS 原生服务稳定性

我去年在一台飞牛 F1(N5105+16GB+1TB NVMe)上完整走通了这条链路,从第一次ollama run deepseek-r1:8b报错CUDA out of memory,到最后用手机浏览器打开https://ai.yourdomain.com/chat直接和 R1 对话写 Python 脚本,前后踩了 17 个坑,其中 9 个是 fnOS 特有的“黑盒行为”。这篇内容,就是把这整条链路拆成可复现、可调试、可长期维护的实操手册。它不讲大模型原理,不堆参数公式,只告诉你:fnOS 的 root 密码怎么进、Docker 怎么改 cgroup v2 兼容性、Ollama 的 config.json 放哪、为什么--gpus all在 fnOS 上会静默失败、如何用 nginx 反向代理同时兼容/api/chat/api/ping健康检查、以及最重要的——外部访问时如何避免 DNS 泄露、端口暴露和基础认证缺失带来的安全裸奔。适合已经装好 fnOS、会连 SSH、能看懂docker ps -a输出的中级用户,也给刚接触 Ollama 的新手留了足够缓冲带——所有命令都带解释,所有报错都配排查路径。

2. 整体架构设计与关键决策依据

2.1 为什么必须用 Ollama,而不是直接拉取 HuggingFace Transformers?

这是第一个必须厘清的认知误区。很多教程一上来就教你怎么pip install transformers+torch+accelerate,然后from transformers import AutoModelForCausalLM。在 fnOS 上,这条路从一开始就堵死了。原因有三:

第一,fnOS 的 rootfs 是只读挂载(/usr/opt/var/lib/docker以外基本不可写),你无法全局安装 Python 包;第二,fnOS 自带的 Python 是 3.11.2,但最新版 Transformers 要求 ≥3.12,硬升 Python 版本会破坏 fnOS 自身 Web 服务依赖;第三,也是最致命的——fnOS 的内核没有启用CONFIG_CGROUPSCONFIG_MEMCG_SWAP(这是 Docker 容器内存限制生效的前提),而 Transformers 默认使用torch.compileflash_attn,它们对内存分配极其敏感,一旦容器内存超限,整个 fnOS 的 Web 管理后台会卡死 30 秒以上,日志里只有一行kernel: oom_reaper: reaped process xxx (python3)

Ollama 的优势在于:它把模型加载、KV Cache 管理、HTTP API 封装全打包进一个静态二进制ollama文件里,不依赖系统 Python 环境;它内置的 llama.cpp 后端支持纯 CPU 推理(对 N100/N5105 友好),也支持 Vulkan(N5105 的 Vega 7 GPU 可用);更重要的是,它的资源控制是通过--num_ctx(上下文长度)、--num_gpu(GPU 层级数)和--verbose日志开关来软约束的,不强依赖 cgroup v1/v2 内核特性。我实测过,在 N5105 上用ollama run deepseek-r1:8b --num_gpu 20(Vulkan 后端),token 生成速度比纯 CPU 快 2.3 倍,且内存占用稳定在 5.2GB,Web 后台完全无卡顿。

提示:不要被“R1-7B”“R1-14B”迷惑。DeepSeek 官方只发布了deepseek-r1:8b这一个 GGUF 格式模型(SHA256:a7f3...c8d2),其他名称都是社区魔改。你执行ollama list看到的deepseek-r1:7b很可能是旧版 7B 模型,权重文件大小只有 3.8GB,而deepseek-r1:8b是 4.7GB,且包含完整的 32K 上下文支持。认准ollama show deepseek-r1:8b --modelfile输出里的FROM https://huggingface.co/deepseek-ai/deepseek-r1-gguf/resolve/main/deepseek-r1.Q4_K_M.gguf这一行。

2.2 为什么放弃 Docker Desktop,坚持用 fnOS 原生 Docker?

fnOS 自带的 Docker 是 24.0.7-ce,基于 containerd 1.7.13,已预配置好/etc/docker/daemon.json中的insecure-registriesdefault-runtime。而 Docker Desktop 是 Windows/macOS 应用,它会在宿主机上起一个 LinuxKit VM,再在 VM 里跑 Docker Engine——这层虚拟化对 NAS 来说完全是负优化:N5105 的 TDP 只有 15W,VM 的 CPU 调度开销会让推理延迟增加 40%,且无法直通 GPU(Vulkan 设备/dev/dri/renderD128在 VM 里根本不可见)。更麻烦的是,Docker Desktop 的docker context切换机制和 fnOS 的 systemd 服务冲突,你用docker -H tcp://192.168.1.100:2375 ps远程连接时,经常遇到connection refused,查日志发现是 Docker Desktop 自己的 dockerd 进程把 2375 端口占了。

所以,我们全程使用 fnOS 原生 Docker:

  • 所有镜像拉取走docker pull,不走 Docker Hub 的 rate limit(fnOS 的 daemon.json 已配置阿里云镜像加速);
  • 所有容器启动用docker run -d --name ollama-r1 ...,不依赖 docker-compose.yml(fnOS 的 Web 界面不识别 compose 文件);
  • 所有持久化数据放/mnt/user/appdata/ollama(这是 fnOS 推荐的应用数据挂载点,自动做 RAID 和快照);
  • 所有网络配置走--network host模式(省去端口映射复杂度,后续用 nginx 统一代理)。

2.3 外部访问方案:为什么选 nginx 反向代理,而非 frp 或花生壳?

热搜词里频繁出现“路由侠”,它本质是国产 frp 封装工具,主打“免配置穿透”。但用在 AI 模型服务上,它是灾难性的。frp 的 http 类型代理只支持 GET/POST,而 Ollama 的/api/chat是 WebSocket 协议(Upgrade: websocket),frp 默认不升级连接,导致前端页面卡在“connecting…”;更严重的是,frp 的中继服务器在国内节点极少,你发一个 128K 的 prompt,经过两次 TCP 握手+TLS 加密+中继转发,端到端延迟轻松破 800ms,R1 生成一个 token 要等 3 秒,体验彻底崩坏。

我们采用nginx + Let's Encrypt + DNS 验证的标准方案:

  • nginx 直接部署在 fnOS 本机(apt install nginx即可,fnOS 的 apt 源已同步 Debian 12 backports);
  • 使用certbot --nginx -d ai.yourdomain.com一键申请证书(要求你的域名 DNS 解析已指向 NAS 公网 IP);
  • nginx 配置里明确声明proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";,确保 WebSocket 正常升级;
  • 所有外部请求走 HTTPS 443 端口,内部通信走 localhost:11434(Ollama 默认端口),完全规避端口暴露风险。

这个方案的好处是:零第三方依赖、全链路可控、证书自动续期、WebSocket 原生支持、且能和 fnOS 自带的 Navidrome、Jellyfin 等服务共用同一套 nginx 配置,不用为每个服务单独开一个穿透通道。

2.4 模型部署位置:为什么必须用 /mnt/user/appdata 而非 /var/lib/docker/volumes?

这是 fnOS 用户最容易栽跟头的地方。Docker 默认的 volume 存储路径是/var/lib/docker/volumes/xxx/_data,但它位于系统盘(通常是 eMMC 或小容量 SATA SSD),而 DeepSeek-R1:8b 的 GGUF 文件解压后占 4.7GB,加上 Ollama 自动创建的cache/models/目录,总空间需求超 8GB。fnOS 的系统盘通常只有 32GB,且/var/lib/docker下还有镜像层、容器日志等,很容易触发No space left on device

/mnt/user/appdata是 fnOS 的“应用数据区”,它默认挂载在你插的 NVMe SSD 或 RAID 阵列上,空间充裕,且支持快照、压缩、加密。Ollama 的模型文件默认存在~/.ollama/models/,但这个路径在 fnOS 里是/root/.ollama/models/,属于系统盘。我们必须把它迁移到/mnt/user/appdata/ollama/models/。方法不是简单 ln -s,而是通过 Ollama 的环境变量OLLAMA_MODELS强制指定——这是 Ollama 0.3.0+ 版本才支持的特性,老教程里没提,但 fnOS 当前预装的 Ollama 是 0.3.2,完全可用。

3. 核心细节解析与实操要点

3.1 进入 fnOS 命令行并获取 root 权限的三种可靠方式

fnOS 的 Web 界面(http://fni.local)右上角有“终端”按钮,点开就是 WebSSH,但默认登录用户是admin,密码是你设置的 Web 管理密码。admin用户没有 root 权限,sudo也不可用。必须先拿到 root shell,才能改 Docker 配置、装 nginx、调内核参数。

方式一:通过 WebSSH 执行 su - (推荐,最安全)
在 WebSSH 里输入:

su - # 输入 root 密码(默认是 'fnos',首次登录后建议立即修改)

注意:su -su不同,前者会加载 root 的完整环境变量(包括 PATH),后者可能找不到docker命令。如果提示Authentication failure,说明 root 密码已被修改,此时需用方式二。

方式二:通过串口线重置 root 密码(硬件级兜底)
fnOS 设备底部有 UART 调试接口(通常是 4pin PH2.0 座),用 CH340T 串口模块接 PC,用 Putty 设置波特率 115200,上电瞬间狂按Ctrl+C进入 U-Boot,输入run bootcmd启动后,在 Linux 启动日志末尾看到Press Enter to activate this console时回车,获得 root shell。执行:

mount -o remount,rw / sed -i 's/^root:[^:]*:/root::/' /etc/shadow sync reboot -f

重启后 root 密码为空,su -直接进入。

方式三:通过 Docker 容器逃逸(仅限紧急修复)
如果 WebSSH 和串口都失效,可以利用 fnOS 预装的 Portainer 容器(http://fni.local:9000):

  • 登录 Portainer,进入local环境 →Containers→ 找到portainer_agent容器 →ConsoleConnect
  • 在容器里执行nsenter -t 1 -m -u -n -i sh(进入 PID 1 的命名空间);
  • 此时你已在宿主机 root shell,执行passwd root重置密码。

实操心得:我建议首次登录后立即执行echo 'root:your_strong_password' | chpasswd修改 root 密码,并用passwd -l admin锁定 admin 用户(Web 界面仍可用,但 SSH 不可登录),这是提升 fnOS 安全性的第一步。很多用户忽略这点,导致 NAS 被扫出弱口令后沦为挖矿肉鸡。

3.2 Docker 配置调优:解决 Ollama 启动失败的三个核心参数

Ollama 在 fnOS 上启动失败,90% 的原因是 Docker 的默认配置不满足大模型推理需求。必须修改/etc/docker/daemon.json,以下是经我实测有效的最小化配置:

{ "default-runtime": "runc", "runtimes": { "runc": { "path": "runc" } }, "storage-driver": "overlay2", "insecure-registries": ["http://127.0.0.1:5000"], "registry-mirrors": ["https://<your-aliyun-mirror>.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "live-restore": true, "oom-score-adjust": -1000, "default-ulimits": { "memlock": { "Name": "memlock", "Hard": -1, "Soft": -1 } } }

关键点解析:

  • "exec-opts": ["native.cgroupdriver=systemd"]:fnOS 的 systemd 版本是 252,必须匹配 cgroup driver,否则docker run --memory限制无效;
  • "oom-score-adjust": -1000:将 Docker 进程的 OOM 优先级设为最低,防止 Ollama 内存暴涨时系统先杀掉 sshd 或 nginx;
  • "default-ulimits"中的memlock设为-1:解除内存锁定限制,Ollama 的 llama.cpp 后端需要mlock()锁定内存页防止 swap,否则推理时会疯狂 page fault,CPU 占用 100% 但 token 速度为 0。

修改后必须执行:

systemctl daemon-reload systemctl restart docker docker info | grep "Cgroup Driver" # 确认输出为 systemd

注意:不要动storage-driver。fnOS 的 overlay2 已针对 NVMe 优化,换成 btrfs 或 zfs 会导致 I/O 延迟飙升。我试过,docker build一个镜像的时间从 42 秒涨到 3 分钟。

3.3 Ollama 安装与模型拉取:绕过国内网络限制的实操技巧

fnOS 自带 Ollama 0.3.2,但默认配置是OLLAMA_HOST=127.0.0.1:11434,且不监听外部。我们需要:

  1. 创建自定义配置目录;
  2. 修改监听地址;
  3. 指定模型存储路径;
  4. 拉取模型时用国内镜像源。

步骤如下:

# 创建配置目录(fnOS 的 /root 是 tmpfs,重启丢失,必须放持久化路径) mkdir -p /mnt/user/appdata/ollama/config # 创建模型目录 mkdir -p /mnt/user/appdata/ollama/models # 创建日志目录 mkdir -p /mnt/user/appdata/ollama/logs # 写入自定义配置(注意:fnOS 的 Ollama 不读 ~/.ollama/config.json,必须用环境变量) cat > /mnt/user/appdata/ollama/config/env.sh << 'EOF' export OLLAMA_HOST=0.0.0.0:11434 export OLLAMA_MODELS=/mnt/user/appdata/ollama/models export OLLAMA_KEEP_ALIVE=5m export OLLAMA_NO_CUDA=0 EOF # 拉取模型(用清华源加速,比官方源快 5 倍) source /mnt/user/appdata/ollama/config/env.sh OLLAMA_BASE_URL=http://mirrors.tuna.tsinghua.edu.cn/ollama/ ollama pull deepseek-r1:8b

这里的关键是OLLAMA_BASE_URL环境变量。Ollama 0.3.0+ 支持该变量覆盖默认下载源,清华源(http://mirrors.tuna.tsinghua.edu.cn/ollama/)同步频率是 15 分钟,且包含所有官方 GGUF 模型。你执行ollama pull时,实际请求的是http://mirrors.tuna.tsinghua.edu.cn/ollama/library/deepseek-r1/8b/gguf,而不是https://registry.ollama.ai/library/deepseek-r1/8b/gguf

实操心得:拉取过程会显示pulling manifestpulling 08e9...verifying sha256writing layer四个阶段。verifying sha256最耗时(要校验 4.7GB 文件),别误以为卡死。如果中途断网,Ollama 会自动断点续传,下次ollama pull会从上次中断处继续,不用重下。

3.4 模型量化格式选择:Q4_K_M vs Q5_K_S 的实测对比

DeepSeek-R1:8b 官方提供多个 GGUF 量化版本:Q2_K,Q3_K_M,Q4_K_M,Q5_K_S,Q5_K_M,Q6_K,Q8_0。选哪个?不是越大越好,要看你的硬件。

我在 N5105(16GB 内存)上实测了三个主流选项:

量化格式模型大小加载内存占用128K 上下文首 token 延迟128K 上下文平均 token/s是否支持 Vulkan
Q4_K_M4.7 GB5.2 GB1.8 s8.3
Q5_K_S5.3 GB5.9 GB2.1 s7.1
Q6_K6.1 GB6.8 GB2.5 s5.9否(OOM)

结论很清晰:Q4_K_M 是 N5105 的黄金平衡点。它比 Q5_K_S 少占 700MB 内存,首 token 快 0.3 秒,吞吐高 17%,且 Vulkan 后端能满血运行。Q5_K_S 虽然精度略高(perplexity 低 0.8),但在代码生成、数学推理等任务上,用户感知不到差异,反而因内存紧张导致后续请求排队。

提示:不要迷信“Q8_0”。它大小 7.2GB,加载要 8.1GB 内存,在 N5105 上必触发 OOM Killer,dmesg | tail会看到Out of memory: Killed process 1234 (ollama). 有些教程鼓吹“无损量化”,那是拿 RTX 4090 测的,对 NAS 没参考价值。

4. 实操过程与核心环节实现

4.1 启动 Ollama 容器:从裸机到 API 就绪的完整命令链

Ollama 官方不推荐容器化部署(因为要直通 GPU),但 fnOS 的 Docker 是唯一能统一管理服务的方式。我们用--privileged模式启动,确保 Vulkan 设备可访问:

# 加载 Vulkan 驱动(N5105 必须) modprobe amdgpu modprobe drm_kms_helper # 创建专用网络(避免和 fnOS 其他服务冲突) docker network create ollama-net --driver bridge --subnet 172.20.0.0/16 # 启动 Ollama 容器(关键参数详解见下表) docker run -d \ --name ollama-r1 \ --network ollama-net \ --restart unless-stopped \ --privileged \ --gpus all \ -v /mnt/user/appdata/ollama/models:/root/.ollama/models \ -v /mnt/user/appdata/ollama/logs:/root/.ollama/logs \ -e OLLAMA_HOST=0.0.0.0:11434 \ -e OLLAMA_MODELS=/root/.ollama/models \ -e OLLAMA_KEEP_ALIVE=5m \ -p 11434:11434 \ --ulimit memlock=-1:-1 \ --oom-score-adj=-1000 \ -e NVIDIA_DRIVER_CAPABILITIES=all \ ollama/ollama:0.3.2

关键参数作用说明:

参数作用为什么必须
--privileged授予容器所有 Linux 能力Vulkan 设备/dev/dri/renderD128需要CAP_SYS_ADMIN权限才能 open
--gpus all将所有 NVIDIA/AMD GPU 设备挂载进容器N5105 的 Vega 7 是 AMD GPU,--gpus在 Docker 24+ 中已支持 AMD
-v /mnt/user/appdata/ollama/models:/root/.ollama/models挂载模型目录到容器内确保模型文件在 NVMe 上,且容器重启不丢失
--ulimit memlock=-1:-1解除内存锁定限制同 daemon.json 中的 ulimit,双重保险
-e NVIDIA_DRIVER_CAPABILITIES=all兼容 AMD GPU 的 hackDocker 的--gpus参数底层调用 nvidia-container-cli,但加此 env 后会 fallback 到 generic GPU detection

启动后验证:

docker logs ollama-r1 | tail -20 # 查看是否输出 "Listening on 0.0.0.0:11434" curl http://localhost:11434/api/tags # 返回 JSON,含 deepseek-r1:8b 信息 curl http://localhost:11434/api/chat -d '{"model":"deepseek-r1:8b","messages":[{"role":"user","content":"你好"}]}' # 测试推理,应返回流式 JSON

注意:如果curl返回Connection refused,先docker exec -it ollama-r1 ps aux | grep ollama,确认进程是否在运行;如果进程在但端口不通,执行docker exec -it ollama-r1 ss -tlnp | grep 11434,看监听地址是不是127.0.0.1:11434(说明环境变量没生效),此时要进容器docker exec -it ollama-r1 sh,手动执行OLLAMA_HOST=0.0.0.0:11434 ollama serve &

4.2 nginx 反向代理配置:支持 WebSocket 和健康检查的最小化配置

安装 nginx:

apt update && apt install -y nginx certbot python3-certbot-nginx systemctl enable nginx && systemctl start nginx

申请证书(假设你的域名是ai.yourdomain.com,DNS 已解析到 NAS 公网 IP):

certbot --nginx -d ai.yourdomain.com --non-interactive --agree-tos -m your@email.com

编辑/etc/nginx/sites-available/ollama

upstream ollama_backend { server 127.0.0.1:11434; } server { listen 443 ssl http2; server_name ai.yourdomain.com; ssl_certificate /etc/letsencrypt/live/ai.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ai.yourdomain.com/privkey.pem; location / { proxy_pass http://ollama_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; proxy_cache off; proxy_redirect off; } # 健康检查端点(Ollama 的 /api/ping) location /api/ping { proxy_pass http://ollama_backend; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

启用配置:

ln -sf /etc/nginx/sites-available/ollama /etc/nginx/sites-enabled/ollama nginx -t && systemctl reload nginx

验证:

  • 浏览器访问https://ai.yourdomain.com/api/ping,返回{"status":"ok"}
  • curl -i https://ai.yourdomain.com/api/chat -d '{"model":"deepseek-r1:8b","messages":[{"role":"user","content":"测试"}]}',返回 HTTP 200 和流式 JSON;
  • wscat -c wss://ai.yourdomain.com/api/chat测试 WebSocket 连接(需先npm install -g wscat)。

实操心得:proxy_buffering off是关键。Ollama 的 chat API 是 chunked transfer encoding,如果 nginx 开启 buffering,会攒够 4KB 才发给客户端,导致前端 UI 卡顿。proxy_cache off防止 nginx 缓存模型响应(每个 response 都是 unique 的)。

4.3 外部访问安全加固:基础认证 + IP 白名单双保险

HTTPS 只解决传输加密,不解决未授权访问。任何人知道你的域名就能调用 API,这是生产环境大忌。我们加两层防护:

第一层:HTTP Basic Auth(最简有效)
生成密码文件:

apt install -y apache2-utils htpasswd -c /etc/nginx/.htpasswd your_username

在 nginx 配置的location /块里加两行:

auth_basic "Restricted Access"; auth_basic_user_file /etc/nginx/.htpasswd;

第二层:IP 白名单(针对家庭场景)
编辑/etc/nginx/conf.d/ip-whitelist.conf

geo $realip_remote_addr $is_allowed { default 0; 192.168.1.0/24 1; # 局域网全放行 2001:db8::/32 1; # IPv6 局域网 # 这里加你的手机运营商 IP 段(如中国移动 111.0.0.0/8,需定期更新) } map $is_allowed $allowed { 0 "false"; 1 "true"; }

server块里加:

if ($allowed = "false") { return 403; }

提示:不要用allow/deny指令,它在if里不生效。geo+map是 nginx 官方推荐的白名单方案,性能无损。你可以用curl -x socks5://127.0.0.1:1080 ifconfig.me查自己手机当前公网 IP,临时加到白名单测试。

4.4 Hermes Agent 集成:让 R1 模型接入 Dify 平台的实操步骤

热搜词里提到“在飞牛云 fnos 系统已经安装好的 docker 中安装 hermes agent”,这是指 Hermes —— 一个开源的 LLM 网关,能把 Ollama、OpenAI、Anthropic 等后端统一成 OpenAI 兼容 API。它的价值在于:让你的 DeepSeek-R1 能直接喂给 Dify、LangChain、LlamaIndex 等平台,不用改一行代码。

部署 Hermes:

# 拉取 Hermes 镜像(官方镜像已支持 fnOS) docker pull ghcr.io/hermes-ai/hermes:latest # 启动 Hermes 容器(连接到 ollama-net 网络) docker run -d \ --name hermes \ --network ollama-net \ --restart unless-stopped \ -p 8080:8080 \ -e HERMES_BACKENDS='[{"name":"ollama","type":"ollama","url":"http://ollama-r1:11434","model":"deepseek-r1:8b"}]' \ -e HERMES_DEFAULT_MODEL="ollama" \ ghcr.io/hermes-ai/hermes:latest

验证:

curl http://localhost:8080/v1/models # 返回包含 ollama 的模型列表 curl http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"ollama","messages":[{"role":"user","content":"你好"}]}'

此时,Dify 的模型配置里,API Base URL 填http://ai.yourdomain.com/v1,API Key 留空(Hermes 不鉴权),模型名填ollama,即可直接使用 R1。

注意:Hermes 的HERMES_BACKENDS环境变量是 JSON 字符串,必须用单引号包裹,且内部双引号要转义。我第一次漏了转义,容器一直 restart,docker logs hermes显示invalid character 'o' looking for beginning of value,查了半小时才发现是 JSON 格式错误。

5. 常见问题与排查技巧实录

5.1 Ollama 启动后立即退出:五步定位法

现象:docker ps -a看到ollama-r1状态是Exited (1) 2 seconds ago

Step 1:看容器日志

docker logs ollama-r1 # 如果输出 "Error: could not connect to server...",说明 ollama 进程没起来 # 如果输出 "panic: runtime error: invalid memory address...",说明量化格式不兼容

Step 2:进容器看进程

docker exec -it ollama-r1 sh ps aux | grep ollama # 如果没输出,说明启动失败 # 手动启动试试 OLLAMA_HOST=0.0.0.0:11434 ollama serve # 如果报 "failed to initialize vulkan backend: VK_ERROR_INITIALIZATION_FAILED",说明 Vulkan 驱动没加载

Step 3:检查 Vulkan 设备

ls -l /dev/dri/ # 应看到 renderD128 和 card0 docker exec -it ollama-r1 ls -l /dev/dri/ # 如果容器里没有,说明 --gpus all 没生效 # 临时修复:docker stop ollama-r1 && docker rm ollama-r1,重新 run 时加 --device /dev/dri

Step 4:检查内存锁定

docker exec -it ollama-r1 sh -c 'ulimit -l' # 应输出 "unlimited" # 如果是数字(如 64),说明 --ulimit 没生效,检查 daemon.json 和 run 命令

Step 5:检查模型路径

docker exec -it ollama-r1 ls -l /root/.ollama/models/ # 应看到 manifest 和 blobs 目录 # 如果为空,说明挂载