小米路由器 Docker-compose 部署 Ollama 大模型:常见报错与排查

背景

小米路由器(基于 OpenWrt 定制)跑 Docker 这件事,本质上就是在路由器 SoC 的有限资源里抠性能。说白了,它和桌面 Linux 不一样:OpenWrt 的网络栈、文件系统、用户权限模型都是「为路由器优化过的」,所以 docker compose 配置稍微带点「标准写法」的惯性,就会踩坑。

OpenWrt

本文聚焦实操中最常碰到的三类报错,把每类问题的现象、原因、排查、解决拆成四段式讲清楚,所有命令都基于截至 2026 年 08 月的主流通行版本,可以直接复制粘贴复用。

本文测试环境:小米 BE6500 Pro(OpenWrt 24.10.0),Docker Engine 28.1.1,docker compose v2.32.0
适用设备:小米 BE6500 Pro、AX9000、万兆路由器等 1GB+ 内存机型;512MB 机型的部分方案会单独标注

问题一:network bridge 冲突导致容器无法启动

现象

docker compose up -d
# Error response from daemon: network bridge is not found
# 或:failed to create network: Error response from daemon: Pool overlaps with other one

可能原因

OpenWrt 的网络架构跟桌面 Linux 不一样:默认网桥由系统(netifd)管理,docker0br-lan 在容器启动前就被预置好了。如果你在 compose.yaml 里手动写 driver: bridge,Docker 守护进程会尝试再创建一个 bridge,结果就和 OpenWrt 现有的网桥撞车。

排查命令

# 查看当前 Docker 网络
docker network ls

# 查看已存在的网桥
ip link show | grep -E 'docker|br-lan|br-'

# 查看 Docker 守护进程配置
cat /etc/docker/daemon.json

解决步骤

1. 先看现状,确认是哪个网桥冲突

ip link show | grep docker
# 通常能看到 docker0 已存在

docker network ls

2. 改用默认网络(最简单的修复)

OpenWrt 下最稳的写法是干脆不写 networks 块,让 Docker 自动用默认网络:

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - ./ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0

注:当前主流的 docker compose v2 已经不再需要 version 字段。

3. 如果一定要手动指定网络,记得避开 OpenWrt 的网段

networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16

进阶:多容器互联(Ollama + OpenWebUI)

如果同时部署 Ollama 和 OpenWebUI,建议建一个专用网络让两个容器互通:

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"   # 对外暴露
    volumes:
      - ./ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0
      - OLLAMA_ORIGINS=*    # 允许跨域访问
    networks:
      - ollama-net

  openwebui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: openwebui
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    depends_on:
      - ollama
    networks:
      - ollama-net

networks:
  ollama-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16

常见变体报错对照表

报错信息 原因 解决方案
network bridge not found 手动指定了不存在的网络名 改用默认网络或先 docker network create
port is already allocated 11434 端口被占用 换端口如 11435:11434 或 kill 占用进程
pool overlaps with other one 自定义网段和现有网桥冲突 换用 172.28.0.0/16 等冷门段
driver failed programming external connectivity iptables 规则冲突 重启 Docker 服务 /etc/init.d/dockerd restart

问题二:存储卷路径权限拒绝

现象

docker compose up -d
# Error response from daemon: Access denied on path /root/.ollama
# 或容器启动后日志反复报:mkdir /root/.ollama: permission denied

可能原因

OpenWrt 上 Samba、NFS 共享目录归属 nobody:nogroup,而 Docker 容器内默认以 ollama(uid 1000)进程跑,挂载卷写入时宿主权限校验失败。说白了就是「容器用户 uid」和「宿主 nobody」对不上。

原理说明

小米路由器 OpenWrt 的 /mnt/storage 这类挂载点,通常被 Samba 服务以 nobody 身份共享,目录属主是 nobody:nogroup。容器里跑的是 Ollama 进程(默认 uid 1000),它看到的挂载目录权限位是不允许 1000 写入的,于是就 permission denied

排查命令

# 查看目录归属
ls -la /mnt/storage/

# 查看容器内默认用户
docker exec ollama id

# 查看当前用户映射
cat /etc/subuid
cat /etc/subgid

解决步骤

1. 手动建目录并放开权限(粗暴但有效)

mkdir -p /mnt/storage/ollama_data
chmod -R 777 /mnt/storage/ollama_data

2. compose 里显式指定 root(关键的一行)

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - /mnt/storage/ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0
    user: "0:0"   # 强制 root 权限解决写入问题

3. 用 tmpfs 临时测一下是不是真权限问题

volumes:
      - type: tmpfs
        target: /root/.ollama

进阶方案:用命名卷绕开文件系统权限怪相

ext4/overlay 的挂载权限怪相(小米路由器刷机分区有它的特殊性),用 Docker 命名卷能比较优雅地绕过:

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    volumes:
      - ollama_data:/root/.ollama

volumes:
  ollama_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/storage/ollama_data

权限问题自查清单

  • 宿主机目录已创建
  • 目录权限为 777 或 755
  • compose 文件中显式指定 user: "0:0"
  • 尝试 /etc/init.d/dockerd restart
  • 排查日志:docker logs ollama

问题三:内存不足导致 OOM Kill

现象

docker compose up -d
# dmesg 中看到类似:
# oom-kill: ... ollama invoked oom-killer, gfp_mask=0x100cca
# 容器状态显示 Exited (137)

老实讲,第一次部署的时候我直接 ollama pull llama3.1:8b,结果没几秒就被 OOM 干了,瞬间破防。后来老老实实换 1B–3B 段位的模型,体感完全两个世界。

可能原因

小米路由器内存通常 512MB–2GB 之间,跑 7B 量化模型(Q4_K_M)勉强够,加载更大模型不加 swap 就直接被内核 OOM Killer 干掉。

原理说明

Linux OOM Killer(Out-of-Memory Killer)是内核兜底机制:物理内存 + swap 全部耗尽时,内核根据 oom_score 杀掉得分最高的进程。Docker 容器虽然有 mem_limit,但若没设置或 swap 缺失,宿主本身被 OOM 时容器也会跟着死。OpenWrt 系统服务(dnsmasq、uhttpd、firewall 等)常驻吃 200–300MB,剩给 Docker 的空间本来就紧。

排查命令

# 查看内存总量与可用
free -m

# 查看 swap 状态
swapon -s

# 看 dmesg 的 OOM 记录
dmesg | grep -i oom

# 容器维度的内存占用
docker stats --no-stream

# 实时内存监控
watch -n 1 'free -m'

# 看容器是否被 OOM 标记
docker inspect ollama | grep -i oom

解决步骤

1. 先看家里内存和 swap 现状

free -m
swapon -s

2. 加 swap 文件(路由器一般默认没有)

dd if=/dev/zero of=/mnt/storage/swapfile bs=1M count=2048
chmod 600 /mnt/storage/swapfile
mkswap /mnt/storage/swapfile
swapon /mnt/storage/swapfile

3. 给容器加硬性内存限制(保守值)

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - /mnt/storage/ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0
      - OLLAMA_NUM_PARALLEL=2
      - OLLAMA_MAX_LOADED_MODELS=1
    mem_limit: 750m
    mem_reservation: 512m
    user: "0:0"

4. 拉小模型,别硬上 7B

docker exec -it ollama ollama pull phi3:mini         # 约 2.3GB
docker exec -it ollama ollama pull llama3.2:1b      # 约 1.3GB
docker exec -it ollama ollama pull deepseek-r1:1.5b # 适合 1GB 内存机型
docker exec -it ollama ollama pull qwen3:1.7b       # 中文场景表现稳
路由器内存配置参考表

路由器型号 内存 推荐模型(截至 2026 年 08 月) 最大并发
小米 AX3600 512MB phi3:mini, llama3.2:1b 1
小米 AX9000 1GB qwen3:1.7b, llama3.2:3b, deepseek-r1:1.5b 1–2
小米 BE6500 Pro 1GB qwen3:1.7b, mistral:7b (Q4) 2
小米万兆路由器 2GB llama3.2:3b, deepseek-r1:7b, qwen3:4b 3

BE6500 Pro 实测数据(截至 2026 年 08 月):空载系统约 280MB,加 swap 后跑 qwen3:1.7b 推理峰值约 620MB,单请求响应延迟 1–3 秒(视 prompt 长度),本地对话场景完全够用。

2026 年适配路由器部署的小模型横评

最近这一年边缘侧部署热度是真高,下面这几个 1B–3B 段位的模型,是我自己在 BE6500 Pro 上跑过、且截至 2026 年 08 月社区评价也比较稳的(长尾关键词:OpenWrt 部署 DeepSeek、小米路由器本地大模型、边缘 AI 部署):

模型 体积(Q4 量化) 中文能力 英文能力 内存占用参考 推荐场景
deepseek-r1:1.5b ~1.0 GB ★★★★ ★★★ ~400MB 推理任务、轻量 Agent
qwen3:1.7b ~1.2 GB ★★★★★ ★★★★ ~500MB 中文对话首选
llama3.2:1b ~0.8 GB ★★ ★★★★ ~350MB 英文场景、轻量补全
llama3.2:3b ~2.0 GB ★★★ ★★★★★ ~750MB 1GB+ 内存机首选
phi3:mini ~2.3 GB ★★★ ★★★★ ~850MB 综合问答
gemma3:1b ~0.8 GB ★★★ ★★★★ ~350MB 低内存兜底

注:内存占用为推理 + 模型加载后的峰值参考,实际视 prompt 长度、并发数有 ±100MB 浮动。

个人推荐优先级(BE6500 Pro / 1GB 内存场景):

  1. 中文对话 → qwen3:1.7b
  2. 推理 / 数学 → deepseek-r1:1.5b
  3. 英文任务为主 → llama3.2:3b

模型拉取、更新、备份流程

拉取模型

# 进入容器拉取
docker exec -it ollama ollama pull <model_name>

# 列出已下载
docker exec -it ollama ollama list

更新模型

# 镜像更新
docker compose pull ollama
docker compose up -d

# 模型单独更新(Ollama 0.5+ 支持)
docker exec -it ollama ollama pull <model_name>

备份已下载的模型

模型文件本身存放在 /mnt/storage/ollama_data/models/ 下(按前文配置),建议定期打包:

# 整机备份
tar -czf ollama_models_$(date +%Y%m%d).tar.gz /mnt/storage/ollama_data/models

# 迁移到另一台设备
scp ollama_models_*.tar.gz root@<新设备IP>:/mnt/storage/

删除不再用的模型

docker exec -it ollama ollama rm <model_name>

关键环境变量调优建议

Ollama 容器有几个环境变量对路由器这种「内存敏感」场景特别重要:

变量 作用 推荐值
OLLAMA_NUM_PARALLEL 最大并发推理数 1–2(视内存)
OLLAMA_MAX_LOADED_MODELS 同时缓存的模型数 1(路由器只能这样)
OLLAMA_KEEP_ALIVE 模型加载后保留时长 "5m"(默认 5 分钟)
OLLAMA_HOST 监听地址 0.0.0.0(局域网可访问)
OLLAMA_ORIGINS 跨域白名单 *(OpenWebUI 用)
OLLAMA_DEBUG 调试日志 1(排查时临时开)

完整示例:

environment:
  - OLLAMA_HOST=0.0.0.0
  - OLLAMA_ORIGINS=*
  - OLLAMA_NUM_PARALLEL=1
  - OLLAMA_MAX_LOADED_MODELS=1
  - OLLAMA_KEEP_ALIVE=5m

完整可用的 docker compose 配置(基于 BE6500 Pro 实测)

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - /mnt/storage/ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0
      - OLLAMA_ORIGINS=*
      - OLLAMA_NUM_PARALLEL=1
      - OLLAMA_MAX_LOADED_MODELS=1
      - OLLAMA_KEEP_ALIVE=5m
    mem_limit: 768m
    mem_reservation: 512m
    user: "0:0"

  openwebui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: openwebui
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    depends_on:
      - ollama
    networks:
      - ollama-net

networks:
  ollama-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16

说明:当前主流的 docker compose v2 已不再需要 version 字段,上面配置可直接使用。

总结

说白了,小米路由器跑 Ollama 这事,本质就是三个坑:网络别瞎配、权限别僵持、内存量力而行。把这三个点踩准了,1GB 内存跑个小模型对话是完全没问题的。

如果你打算更进一步(比如加 NPU offload、跑 Agent 框架),建议先去小米万兆路由器那种 2GB+ 的机型上玩,BE6500 Pro / AX9000 这种 1GB 机还是老老实实跑 1B–3B 模型,省心也省电。

常见问题 FAQ

Q1:小米 BE6500 Pro 能不能跑 7B 模型?
A:能跑,但得加 2GB 以上的 swap,并且 mem_limit 调到 1.5g 以上。响应速度会比较慢,单请求推理可能 10 秒以上。日常对话还是 1B–3B 段位更舒服。

Q2:swap 文件放 /mnt/storage 还是 /tmp
A:建议放 /mnt/storage,因为路由器内部 flash 寿命有限,长期 swap 写入会刷坏存储分区。/mnt/storage 通常是外接 USB 存储,扛得住。

Q3:Ollama 和 OpenWebUI 必须一起装吗?
A:不一定。Ollama 单独启动后,通过 11434 端口提供 API,可以被任何兼容客户端使用(甚至 SSH 里 curl 调用也行)。OpenWebUI 只是其中一个比较舒服的 Web 前端。

Q4:怎么备份已下载的模型?
A:模型文件存放在 /mnt/storage/ollama_data/models/(按本文配置),直接 tar 打包就行,跨设备迁移 scp 即可。

Q5:BE6500 Pro 跑 Ollama 实际功耗多少?
A:在 BE6500 Pro 上加挂两个容器(Ollama + OpenWebUI),空载 + 待机时总功耗约 +1.5W,跑推理峰值约 +3W,整体路由器功耗仍在 12W 以内。

Q6:能跑 DeepSeek-R1 蒸馏版吗?
A:可以。deepseek-r1:1.5b 体积约 1GB,是 1GB 内存路由器上推理 / 数学场景的优选;deepseek-r1:7b 推荐放到万兆路由器(2GB+)上。

Q7:怎么确认容器没被 OOM Kill?
A:dmesg | grep -i oom 看有没有 Killed process 记录;docker inspect ollama | grep OOMKilled 字段为 true 即被杀。

Q8:log 提示 iptables 报错怎么办?
A:OpenWrt 默认 nftables 后端,docker 可能用了旧 iptables,编辑 /etc/docker/daemon.json 加入:

{
  "iptables": false
}

然后 /etc/init.d/dockerd restart 即可。

Q9:升级 OpenWrt 后容器全挂怎么办?