如何在VPS上安全部署Ollama并自托管大模型
7B模型约需8 GB内存,CPU速度约为每秒4到10个token。本文介绍如何在VPS运行Ollama,通过127.0.0.1:11434/v1调用,并保持11434端口关闭,避免未授权访问。
构建内容
在您拥有的服务器上运行一个单独的开放权重语言模型,通过 HTTP API 提供响应;如果需要,也可以在浏览器中使用聊天页面。Ollama 负责下载模型、将模型加载到内存,并在 http://127.0.0.1:11434 上提供请求服务。安装只需执行一个命令。真正复杂的部分在其他方面:选择 VPS 实际能够装入 RAM 的模型,并避免将未经身份验证的推理服务器意外发布到整个互联网。
先说明两个重要限制。仅使用 CPU 的 VPS 运行小型模型时速度较慢,而且 API 完全没有内置身份验证功能。下文会详细介绍这两点,因为它们都是最容易造成问题的地方。
用实际数字检查资源需求
模型的内存占用大致等于模型文件大小,加上约 1 GB 的运行时开销,再加上上下文窗口所需的额外内存。Ollama 的默认模型采用 4-bit 量化(标记为 Q4),每 10 亿个参数约占 0.5 GB RAM。因此,计算方式很简单,而且它决定了模型是否能运行。最后一项由您自行设置:将 num_ctx 调高到 Ollama 较小的默认值以上可以容纳更长的提示词,但会增大 RAM 中的 KV cache,因此应先确定上下文长度,再判断模型是否适合。
3B 模型(例如 llama3.2:3b)的下载大小约为 2 GB,运行时需要约 4 GB 可用 RAM。7B 或 8B 模型(例如 mistral:7b 或 llama3.1:8b)在磁盘上约占 5 GB,需要约 8 GB RAM;若要运行得更稳定,建议使用 16 GB RAM。13B 或 14B 模型大约需要 16 GB RAM。30B 到 70B 范围内的模型需要大内存服务器,或者更现实地说,需要 GPU。在 CPU VPS 上,这类模型要么无法装入内存,要么响应速度慢到无法使用。
接下来是速度,因为这一点经常被低估。CPU 推理受内存带宽限制,而不是受时钟频率限制;共享 vCPU VPS 的内存带宽通常有限。预计速度为个位数到十几 tokens/秒:7-8B Q4 模型可能达到 4 到 10 tokens/秒,3B 模型可能达到 10 到 25 tokens/秒。GPU 的速度大约快一个数量级。这些只是有意提供的粗略数据。更可靠的做法是测量您自己的服务器,下面的运行步骤会说明如何操作。请相信您的 eval rate,不要相信任何文章中的数字,包括本文中的数字。
实际结论是:如果您能接受这种速度,CPU 上的小型量化模型确实适合用于起草、摘要和分类。若需要更大的模型或更快的速度,请为 GPU 实例预留预算。如果您希望在一个具体模型上完整演示这些计算,在 VPS 上运行 Nemotron 3.5 Lightning会说明应拉取哪个 tag、实际需要多少 RAM,以及仅使用 CPU 是否能满足速度要求。
要将具体模型与具体服务器进行比较,请在此处估算其内存占用:
安装 Ollama
有两种简便方法。在裸 VPS 上,官方脚本最简单:
curl -fsSL https://ollama.com/install.sh | sh此脚本会创建名为 ollama 的系统用户,将二进制文件安装到 /usr/local/bin/ollama,并注册名为 ollama.service 的 systemd 服务。该服务会在系统启动时启动,并绑定到 127.0.0.1:11434。确认服务已运行:
systemctl status ollama
ollama --version如果已经运行 Docker,也可以使用容器:
docker run -d --name ollama \
-p 127.0.0.1:11434:11434 \
-v ollama:/root/.ollama \
--restart always \
ollama/ollama注意端口映射中的 127.0.0.1: 前缀。这样会仅将端口绑定到 localhost。若改为 -p 11434:11434,则会将端口发布到所有网络接口。这正是安全章节警告的错误配置。选择一种安装方法即可。不要同时运行脚本和容器,否则两个进程会争用同一个端口。
拉取并运行您的第一个模型
ollama pull llama3.2:3b
ollama run llama3.2:3bpull 将模型层下载到磁盘(此模型约占 2 GB)。run 将这些层加载到内存,并显示 >>> 提示符。输入一个问题。加载权重并将其从磁盘读入 RAM 时,第一个 token 可能需要几秒钟,之后答案会以流式方式输出。输入 /bye 退出聊天;Ollama 会继续在后台运行。
查看已加载的内容及其运行方式:
ollama psPROCESSOR 列显示实际情况。100% CPU 表示未使用 GPU,这正是运行缓慢的原因。使用 verbose 标志测量实际速度:
ollama run --verbose llama3.2:3b "Write two sentences about Linux."末尾输出的 eval rate 行表示此硬件上的每秒 token 数。这是规划时应采用的数值。
模型存放位置,以及需要购买多大的磁盘
由脚本安装并作为服务运行时,模型存放在 ollama 用户的主目录中:
sudo du -sh /usr/share/ollama/.ollama/models使用您自己的用户以交互方式运行时,模型存放在 ~/.ollama/models 中。在容器中,模型存放在名为 ollama 的卷中。这一点很重要,因为量化权重会迅速占用磁盘空间:3B 模型约占 2 GB,7-8B 模型约占 5 GB,14B 模型约占 9 GB。为了进行比较而拉取 4 个模型后,可能在不知不觉间占用 20 GB。请根据计划保留的模型确定磁盘容量,并使用 ollama rm <model> 删除其余模型。如果同一台 VPS 还运行着自身需要大量存储空间的服务,例如保存照片库的 PhotoPrism 或 Immich,请先从可用空间中扣除这些服务占用的空间,再将剩余空间视为实际的模型预算。
作为受控服务运行
安装脚本已注册 ollama.service,因此它会在系统启动时自动重启,无需额外配置。通常需要调整的是模型驻留时间;在某些环境中,还需要调整绑定地址。这两项配置都应写入 systemd drop-in,这样 Ollama 升级时不会覆盖它们:
sudo systemctl edit ollama.service在编辑器显示的 [Service] 标题下添加以下内容:
[Service]
Environment="OLLAMA_KEEP_ALIVE=30m"OLLAMA_KEEP_ALIVE 表示模型在最后一次请求后继续驻留内存的时间(默认 5 分钟)。如果整天都会查询某台服务器,请增大该值,避免每次都重新加载权重;如果服务器内存紧张,请将其设为 0,让请求完成后立即释放 RAM。若希望模型永久驻留,而不是仅在固定时间窗口内驻留,请参阅永久保持 Ollama 模型加载,其中介绍了每个请求的 keep_alive 字段,以及如何在重启后重新预热权重,而不是等到第一次请求时才进行缓慢加载。systemctl edit 会重新加载单元文件,因此需要重启服务才能应用更改:
sudo systemctl restart ollama最重要的安全事项
默认情况下,Ollama 绑定在 127.0.0.1:11434,因此只有 VPS 本机上的进程可以访问它。这个默认设置是正确的。请保持不变。
该 API 没有任何身份验证。完全没有。它没有 API key、登录功能、速率限制或允许列表。任何能够访问 11434 端口的用户,都可以运行你已拉取的任何模型、拉取新模型、删除模型,还可以让 CPU 或 GPU 长时间满负载运行。Shodan 等扫描器会将开放的 Ollama 实例按数千个编入索引,暴露的实例通常会在数小时内被发现并滥用。
因此,绝对不要犯这个错误:不要设置 OLLAMA_HOST=0.0.0.0 并在防火墙中开放 11434。这样会将未经过身份验证的推理服务器暴露给整个互联网。无论如何配置,直接在 0.0.0.0 上使用 11434 都不安全,因为 Ollama 没有可配置的身份验证功能,身份验证机制根本不存在。这条规则针对的是这一特定服务,并不意味着任何端口都不能开放:用于远程桌面的自托管 RustDesk 中继必须接受公网流量才能正常工作,而它之所以可以这样部署,是因为它自带基于密钥的身份验证,并明确记录了所需的少量端口;Ollama 完全没有这些机制。
从其他设备访问模型时,有 3 种安全方式:
- 保持本地访问。如果唯一的调用方是同一 VPS 上的另一个程序、cron 脚本、机器人,或将工具连接到模型的 MCP 服务器,请将绑定地址保持为
127.0.0.1,让该程序调用http://127.0.0.1:11434。这样不会暴露任何服务,也不需要其他配置。 - 通过私有隧道访问。将 VPS 加入你自行托管的 WireGuard VPN,把
OLLAMA_HOST设置为隧道地址(例如10.8.0.1,而不是0.0.0.0),这样只有 VPN 对等端可以连接。公网仍然无法看到 11434 端口上的服务。 - 在前面部署需要身份验证的反向代理。在 nginx、Traefik 或 Caddy 上终止 TLS,并要求密码或令牌,然后将请求代理到
127.0.0.1:11434。Ollama 继续绑定 localhost,只有代理监听公网端口。这与在任何本地服务前面的 nginx 上配置 Let's Encrypt 证书的方式相同。
下一步的聊天界面会提供反向代理方案,并附带实际的登录功能。
使用 Open WebUI 添加聊天界面,并置于 TLS 之后
Open WebUI 是一个自托管聊天界面。将它运行在 Docker 中,并让它连接本地 Ollama:
docker run -d \
--name open-webui \
--network=host \
-e OLLAMA_BASE_URL=http://127.0.0.1:11434 \
-v open-webui:/app/backend/data \
--restart always \
ghcr.io/open-webui/open-webui:main在 Linux VPS 上,--network=host 标志很重要。它会让容器使用主机的网络命名空间,因此容器中的 127.0.0.1 就是主机自己的回环地址,容器可以通过 127.0.0.1:11434 访问 Ollama,而无需让 Ollama 监听其他网络接口。你会在其他地方看到的桥接网络配置,即使用 OLLAMA_BASE_URL=http://host.docker.internal:11434 配置 --add-host=host.docker.internal:host-gateway 的方式,在这里无法工作:该名称会解析为 Docker 桥接网关,而主机上绑定到 127.0.0.1 的服务无法通过桥接网络访问。因此,Open WebUI 只会持续报告无法连接到 Ollama。
使用主机网络的代价是,Open WebUI 现在会在主机的 8080 端口上监听所有网络接口;任何 -p 映射都会被丢弃,Docker 也会输出相应警告。因此,应在主机防火墙和云服务商防火墙上都关闭 8080,并让 TLS 反向代理成为唯一的公网入口。首次访问 Open WebUI 时,它会要求您创建管理员账户。该账户就是身份验证层,因此应设置强密码。
要从笔记本电脑通过 HTTPS 打开聊天界面,请在 127.0.0.1:8080 前配置 TLS 反向代理。如果您已经在这台主机上为多个 Docker 应用配置路由,使用 Traefik 为多个应用自动配置 TLS 是最合适的方案:一个标签配置块即可申请证书,并将 chat.example.com 路由到 Open WebUI。相同的按应用配置标签的路由方式也适用于主机上的其他浏览器前端,无论是状态仪表板,还是更具娱乐性的应用,例如 Halcyon,它会将 Jellyfin 媒体库呈现为 90 年代的录像带出租店;每个应用都使用独立的主机名,并置于各自的登录保护之后。安全部分的规则仍然适用:公网端口和登录由代理负责,Ollama 继续监听 localhost,而 Open WebUI 自身的 8080 继续受到防火墙保护。
使用代码调用 OpenAI 兼容端点
Ollama 在 /v1 提供 OpenAI Chat API 的一个子集,因此大多数 OpenAI 客户端库只需修改两项即可使用:基础 URL 和临时密钥。
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:11434/v1", api_key="ollama")
resp = client.chat.completions.create(
model="llama3.2:3b",
messages=[{"role": "user", "content": "Name three Linux distributions."}],
)
print(resp.choices[0].message.content)客户端库要求提供 api_key,但 Ollama 会忽略它,因此可以使用任意字符串。model 必须是你已经拉取的模型名称;如果名称未知,则会返回 model "x" not found, try pulling it first。直接使用 curl 调用的原理相同:
curl http://127.0.0.1:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"llama3.2:3b","messages":[{"role":"user","content":"Hello"}]}'这也是将模型接入代理工具和编辑器工具的方式。如果你已经在这台服务器上进行开发,本地模型可以与 在 VPS 的 tmux 中运行 Claude Code 一起为脚本和插件提供支持,让低成本的私有草稿处理不必调用付费 API,同时将复杂推理交给托管模型。
故障模式及你将看到的确切字符串
进程在生成过程中被“Killed”。 启动大型模型后,终端输出 Killed,或者服务器日志显示 llama runner process has terminated: signal: killed。这是因为模型需要的 RAM 超过了服务器可用内存,Linux OOM killer 将其终止。使用 sudo dmesg | grep -i oom 确认原因;其中应能看到类似 Out of memory: Killed process ... (ollama) 的一行。解决方法是改用更小或量化程度更高的模型,例如使用 llama3.2:3b,而不是 13B 模型;也可以添加 swap,使仅略微超出物理 RAM 的负载能够缓慢完成,而不是直接退出。swap 会将瞬时崩溃变成缓慢响应,但不能让 70B 模型在 4 GB 内存上变得实用。除非当时正在查看终端,否则通常不会看到这次终止。对于从其他位置查询的服务器,可将一个 OnFailure= 单元附加到 ollama.service,向您托管的 ntfy 服务器发送推送告警,这样进程终止时会立即收到通知,而不必等到下一次请求才发现问题。
“Error: model requires more system memory”。 Ollama 拒绝启动模型,并输出 Error: model requires more system memory (X GiB) than is available (Y GiB)。这是上述崩溃的温和版本:Ollama 先完成内存计算并停止,而不是让 OOM killer 终止进程。它还会直接显示所需内存和可用内存这两个数值。选择所需内存低于可用 RAM 的模型(使用 free -h 检查),缩短上下文长度,或迁移到更大的 VPS。没有任何 flag 能让模型适应不足的内存,因为这些内存需求是真实存在的。
第一个 token 等待很久,之后运行正常。 冷启动模型时,前五到三十秒没有输出,之后开始正常流式返回。这段暂停时间用于首次将模型权重从磁盘加载到 RAM,存储速度较慢时会更加明显。加载完成后,模型会在 OLLAMA_KEEP_ALIVE 指定的时长内常驻内存,因此第二个提示会立即得到响应。如果这些间隔时间令人困扰,可以增大该值;使用 ollama ps 查看当前是否已有模型加载。
所有操作都很慢。 每秒只能生成 10 个或更少的 token,且没有任何错误。这正是 CPU 推理的正常表现。ollama ps 显示 100% CPU,表示没有 GPU。这不是 bug,也没有任何设置可以修复,因为瓶颈在内存带宽,而不是配置错误。可以改用更小的模型、接受当前速度,或迁移到 GPU 实例。在判断系统是否异常前,先使用 --verbose 测量实际生成速率。如果等待时间来自回答长度而不是生成速率,使用 num_predict 限制回答长度可以避免模型持续生成你根本不会阅读的 token,耗时数分钟。
从另一台机器连接时被拒绝。 在笔记本上会得到 curl: (7) Failed to connect to <ip> port 11434: Connection refused。这是预期行为:Ollama 只绑定 localhost。不要通过绑定 0.0.0.0 来“修复”此问题,因为这正是前文所述的暴露配置错误。应通过 VPN 或带身份验证的代理访问该模型。
你已将 11434 暴露到互联网。 如果确实设置了 OLLAMA_HOST=0.0.0.0、打开了防火墙规则,随后发现出现从未发起过的模型拉取操作,或 CPU 被未知客户端占用至 100%,说明该服务已被发现并被他人使用。这是最严重的配置错误,不是边缘情况。重新绑定到 127.0.0.1 或 VPN 地址,在防火墙中关闭 11434,并在前面配置身份验证。应假设该地址开放期间,任何能够访问它的内容都已被陌生人查询。
备份与升级
需要保留的状态很少。模型可以重新下载,因此值得备份的只有 Open WebUI 的数据卷、账户、聊天记录、设置,以及您编写的任何 systemd drop-in 配置。使用临时容器备份数据卷:
docker run --rm -v open-webui:/data -v "$PWD":/backup alpine \
tar czf /backup/open-webui.tgz -C /data .通过重新运行安装脚本升级 Ollama;使用 docker pull ghcr.io/open-webui/open-webui:main 升级 Open WebUI,然后重新创建容器。不要长期固定任何版本:模型质量和运行时变化都很快,因此请阅读发行说明,并在自己的服务器上重新进行基准测试,不要直接相信上一季度的数据。
FAQ
我真的可以在仅提供 CPU 的 VPS 上运行 LLM 吗?
可以,但有一定限制。3B 到 8B 范围内的小型量化模型可以在 CPU 上运行,适合用于起草、摘要和分类,但速度较慢。在共享 vCPU 上,速度通常只有个位数到十几 tokens 每秒。13B 及以上的模型运行速度会非常慢,或者根本无法装入 RAM。若需要更高速度或更大的模型,则需要 GPU 实例。
每个模型需要多少 RAM?
对于默认的 4-bit 量化模型,可以按以下规则粗略估算:权重每 10 亿个参数约需 0.5 GB RAM,此外还需约 1 GB 的开销,并为上下文额外预留一些空间。因此,3B 模型需要约 4 GB 可用内存,7-8B 模型需要约 8 GB,14B 模型需要约 16 GB。使用 free -h 检查可用内存余量,并为操作系统和服务器上的其他程序预留空间。
Ollama API 是否经过身份验证?
没有。Ollama 没有内置身份验证、API 密钥或速率限制。任何可以访问端口 11434 的人都能完全控制它。这正是它默认绑定到 127.0.0.1 的原因,也是您绝不能将 0.0.0.0 上的 11434 端口暴露到互联网的原因。请通过本机、私有 VPN 或添加登录功能的反向代理访问它。
如何添加 Web 聊天界面?
使用 --network=host 在 Docker 中运行 Open WebUI,使其共享主机的 loopback,并访问位于 http://127.0.0.1:11434 的原生 Ollama。然后在其 8080 端口前配置 TLS 反向代理,以便从笔记本电脑访问。保持防火墙关闭 8080,确保反向代理是唯一的公网入口。Open WebUI 自带的管理员账户提供登录功能,首次启动时设置其密码。
如何从自己的应用程序调用它?
使用 http://127.0.0.1:11434/v1 上的 OpenAI 兼容端点。将任意 OpenAI SDK 指向该基础 URL,将任意字符串作为 API 密钥传入,因为该密钥会被忽略,并将 model 设置为您已拉取的模型名称。除基础 URL 和密钥外,现有的 OpenAI 代码通常无需修改即可运行。