如何在 VPS 上以 rootless Podman 运行 Ollama
使用专用非特权用户、lingering 和 Quadlet 运行 Ollama;配置 SELinux 模型目录标签,重启后自动恢复,并将 11434 端口限制为 loopback,通过 SSH 隧道访问。
在 VPS 上以 rootless Podman 运行 Ollama
要在服务器上的 rootless Podman 中运行 Ollama,必须满足 5 个条件,而桌面环境教程可以跳过这些条件。由专用的非特权用户拥有容器。为该用户启用 lingering,这样退出登录后容器仍会继续运行。使用 Quadlet 文件将容器交给 systemd 管理,使其在重启后自动恢复。对于启用 SELinux 的发行版,为模型目录设置 SELinux 标签。API 仅监听 loopback,并通过 SSH(安全 Shell)隧道访问。
Ollama 是用于运行大型语言模型(LLM)的服务器。它将模型权重存储在磁盘上,将其加载到内存,并在端口 11434 上响应 HTTP 请求。它没有登录功能、API key 或用户账户,因此网络是唯一的访问控制措施。Podman 无需 daemon 且无需 root 即可运行容器,因此即使有内容从容器中逃逸,最初也只能以普通非特权用户身份运行。如果您希望先了解运行时对比,请阅读 Podman 与 Docker 在 VPS 上的差异。如果您希望完全跳过容器,直接在 VPS 上安装 Ollama 是更短的路径。
SSD Nodes 在其镜像中提供 Fedora,而 Fedora 默认同时提供 Podman 和 SELinux(增强安全性的 Linux)。以下每条命令都可在安装了 Podman 5 或更高版本的发行版上运行。
服务器上需要调整笔记本配置的原因
Fedora Magazine 于 2026 年 8 月 5 日发布了一篇清晰介绍此技术栈的教程:在 Fedora Linux 上使用 Podman 运行 Ollama,作者是 Yazan Monshed。这是熟悉这些工具的良好入门教程。但该教程面向笔记本电脑,其中四项配置在具有公网 IP 地址的机器上会产生不同的结果。
- 它使用普通的
podman run -d启动容器。手动启动的容器不会在重启后自动恢复,因为从未配置任何机制来启动它。 - 它使用会不断变化的
ollama/ollama标签。在笔记本电脑上,您会注意到行为发生变化的那一天。在服务器上,您首先可能发现某个脚本在一夜之间停止工作。 - 它使用
-p 11434:11434发布端口,这会绑定所有网络接口。在家庭路由器后面时,互联网无法访问该端口。在 VPS 上,这会变成一个没有密码保护的公网推理 API。 - 它使用您自己的登录用户运行。在服务器上,拥有该容器的账户不应再拥有其他资源,这样即使发生容器逃逸,攻击者也只会进入一个空的主目录。
对于原教程所针对的机器,这些配置都没有问题。当服务器可以从任何地方访问,且没有人坐在服务器前操作时,您只需重新评估这些配置。
创建非特权用户并检查 subuid
Rootless Podman 会将容器内部的用户 ID(UID)映射到主机上一段未使用的 ID 范围。该范围定义在 /etc/subuid 和 /etc/subgid 中。没有这段范围,rootless 容器根本无法启动。
sudo dnf install -y podman # or: sudo apt install -y podman
sudo useradd --create-home --shell /bin/bash --comment "Ollama container owner" ollama
sudo passwd --lock ollama
grep ollama /etc/subuid /etc/subgidgrep 应输出 2 行,分别来自这 2 个文件。每行都应显示一个包含 65536 个 ID 的范围:
/etc/subuid:ollama:100000:65536
/etc/subgid:ollama:100000:65536起始编号会有所不同,这是正常的。如果 grep 没有输出内容,说明 useradd 没有分配 ID 范围。该用户执行第一个 podman 命令时会失败,并显示以下错误:
Error: cannot find UID/GID for user ollama: no subuid ranges found for user "ollama" in /etc/subuid分配一段其他用户未占用的范围,然后告知 Podman 旧映射已失效:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ollama
sudo -iu ollama podman system migrate锁定密码后,任何人都不能直接以 ollama 身份登录。请使用 sudo -iu ollama 从管理员用户切换到该账户。
启用 lingering,使服务在注销后继续运行
用户的 systemd 实例通常在登录时启动,并在注销时停止,/run/user/<uid> 也会随之被删除。该用户拥有的所有 rootless 容器会在同一时刻退出。启用 lingering 后,即使没有关联的会话,用户实例也会继续运行。
sudo loginctl enable-linger ollama
loginctl show-user ollama --property=Linger该命令应输出 Linger=yes。请在创建 unit 之前启用 lingering,因为 unit 所需的目录 /run/user/<uid> 只有在启用 lingering 后才会存在。
还有一个容易被忽略的步骤。sudo -iu ollama 会提供 shell,但不会提供会话总线,因此 systemctl --user 会立即失败:
Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not definedsystemd 会在 $XDG_RUNTIME_DIR/bus 查找用户总线,而 sudo -i 不会设置该变量。请在管理此服务的每个管理员 shell 中手动设置它:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user status模型文件存放位置及磁盘空间规划
Ollama 会将权重写入容器内的 /root/.ollama/models。将用户主目录中的一个目录绑定挂载到该路径后,文件会存放在便于测量的位置:/home/ollama/ollama-data/models。模型文件以内容寻址文件的形式存放在 models/blobs 中,models/manifests 则保存用于标识这些文件的小型索引。如果改用命名卷(Fedora Magazine 的文章采用了这种方式),相同的目录树会位于 /home/ollama/.local/share/containers/storage/volumes/<volume>/_data 下。无论采用哪种方式,ollama pull 和 ollama run 都会将权重写入同一目录树;两个命令的区别仅在于下载完成后是否立即打开聊天会话。
在拉取任何内容前先规划磁盘空间。已发布的下载大小是最低需求。
The data behind this chart
[
{
"label": "gemma3:4b",
"download_gb": 3.3
},
{
"label": "mistral:7b",
"download_gb": 4.4
},
{
"label": "qwen3:8b",
"download_gb": 5.2
},
{
"label": "gemma3:12b",
"download_gb": 8.1
},
{
"label": "qwen3:14b",
"download_gb": 9.3
},
{
"label": "gemma3:27b",
"download_gb": 17
},
{
"label": "qwen3:30b",
"download_gb": 19
}
]这里的 7 行数据均为 ollama.com/library 发布的数值,不是实际在磁盘上测得的大小。此处最小的标签 gemma3:4b 需要下载 3.3 GB。最大的标签 qwen3:30b 需要下载 19 GB。容器镜像还会额外占用 Podman 自身存储中的空间,因此应结合 podman system df 和 df -h /home 一起检查这两个数值。模型加载时通常还需要约等于其文件大小的 RAM,并且需要为上下文窗口预留空间,因此 16 GB VPS 无法运行 19 GB 的模型。
固定镜像标签,并使用完整的镜像仓库名称
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
mkdir -p ~/ollama-data ~/.config/containers/systemd
podman pull docker.io/ollama/ollama:0.32.9使用已发布的版本标签,0.32.9(截至 2026 年),不要使用 latest。固定标签可确保 04:00 重启时使用的二进制文件与测试时相同,因此行为发生变化时,可以确定是您所做的更改导致的。Docker Hub 还会为相同版本发布 -rc 和 -rocm 标签;除非您使用 AMD GPU,否则请选择不带后缀的标签。
同时写明镜像仓库主机名。在 Fedora 上,systemd 单元中的短名称没有可用于提示输入的终端,因此该单元会失败,并显示:
Error: short-name "ollama/ollama" did not resolve to an alias and no unqualified-search registries are defined先手动拉取镜像不是必须的,但这样做很有用,因为它可以将数 GB 的下载过程移出单元的启动超时范围。
重启后仍然存在的 Quadlet 单元
Quadlet 是 Podman 的 systemd 生成器。您需要编写一个 .container 文件,systemd 会在启动时将其转换为服务,因此不再需要 podman generate systemd。将以下内容保存为 /home/ollama/.config/containers/systemd/ollama.container,并将所有者设为 ollama 用户。
[Unit]
Description=Ollama API (rootless)
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/ollama/ollama:0.32.9
ContainerName=ollama
PublishPort=127.0.0.1:11434:11434
Volume=/home/ollama/ollama-data:/root/.ollama:Z
Environment=OLLAMA_KEEP_ALIVE=30m
Environment=OLLAMA_MAX_LOADED_MODELS=1
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target文件名决定服务名称,因此 ollama.container 会变为 ollama.service。
systemctl --user daemon-reload
systemctl --user start ollama.service
systemctl --user status ollama.servicestatus 应显示 active (running)。不要运行 systemctl --user enable ollama.service。该单元不是磁盘上的文件,因此 systemd 会拒绝执行:
Failed to enable unit: Unit file /run/user/1001/systemd/generator/ollama.service is transient or generated.[Install] 部分已经完成这项工作。Quadlet 会在 daemon-reload 期间自行创建开机启动链接,因此该命令不可省略。TimeoutStartSec=900 用于首次启动时仍需拉取镜像的情况,因为默认的 90 秒不足以完成 2 GB 的下载,systemd 会将此次启动终止并标记为失败。OLLAMA_KEEP_ALIVE=30m 会在请求之间将模型保留在内存中,而不是在 5 分钟后将其卸载;相关权衡见将 Ollama 模型保留在内存中。如果这里的 systemd 术语不熟悉,请参阅VPS 上 systemd 服务和计时器的工作方式,了解这些单元本身。
SELinux 下模型目录为何会返回 permission denied
在 Fedora、RHEL、Rocky 和 AlmaLinux 上,SELinux 默认处于强制模式。容器进程运行在 container_t 域中,而用户主目录下的目录标记为 user_home_t。SELinux 策略不允许这两种上下文互相访问,因此 Ollama 无法创建模型目录树,容器也会退出。在这些系统上,getenforce 会输出 Enforcing,拒绝操作会被记录:
sudo ausearch -m avc -ts recent您会看到一行列出域和目标标签:
avc: denied { write } for pid=1842 comm="ollama" name="models" dev="vda1" ino=131077 scontext=system_u:system_r:container_t:s0:c214,c827 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=dir permlisted=0Volume= 行末的 :Z 就是解决方法。它会将主机目录重新标记为 container_file_t,并为其设置一个仅由此容器使用的私有 MCS(多类别安全)类别。小写的 :z 使用共享标签;当两个容器需要读取同一目录时,应使用此选项。
关于 :Z 需要特别注意:它会递归重新标记,而且不会提示。将它指向 /home/ollama 后,该主目录中的每个文件都会被重新标记,从而导致该用户无法访问 SSH 密钥。始终为 :Z 指定一个专用子目录,并确保其中不包含其他内容。命名卷不需要此选项,因为 Podman 创建命名卷时会正确设置标签。如果需要了解更完整的背景,请参阅 服务器上的 SELinux 基础知识,其中介绍了上下文和布尔值。在 Ubuntu 和 Debian 上使用的是 AppArmor,:Z 在这些系统上不起作用,将其保留在单元文件中也不会造成影响。
关闭 11434 端口并通过 SSH 访问 API
PublishPort=127.0.0.1:11434:11434 将主机侧绑定到回环地址。确认这一点:
ss -ltnp | grep 11434
curl http://127.0.0.1:11434ss 的输出必须显示 127.0.0.1:11434。0.0.0.0:11434 或 *:11434 表示该端口已对互联网开放,并且 curl 必须响应 Ollama is running。
请准确区分绑定发生在哪一侧。PublishPort 中的地址是主机地址。在容器内,Ollama 必须继续监听所有接口,这是该镜像的默认设置。设置 Environment=OLLAMA_HOST=127.0.0.1 会将 Ollama 绑定到容器自身的回环地址,而 Podman 会将发布的流量转发到容器的网络地址,因此即使从主机访问,每个请求也都会被拒绝。
开放 11434 端口会带来两方面的风险。Ollama 没有身份验证,任何能访问该端口的人都可以通过 /api/tags 列出您的模型,通过 /api/generate 使用您的 CPU 和带宽配额运行推理,将新模型拉取到您的磁盘,并删除您已有的模型。其次,向远程端口发送的普通 HTTP 流量不会加密提示词和补全内容,路径上的每台机器都可以读取这些内容。如果端口始终不离开本机,这两个问题都会消失。
在工作站上,通过 SSH 转发该端口:
ssh -N -L 11434:127.0.0.1:11434 you@vps.example.com现在,您笔记本电脑上的 http://127.0.0.1:11434 就是服务器上的 Ollama,流量位于 SSH 会话的加密通道内。如果您的笔记本电脑已经运行 Ollama,本地绑定会因 bind [127.0.0.1]:11434: Address already in use 失败;请使用 -L 11435:127.0.0.1:11434,并让客户端连接到 11435。
如果浏览器客户端需要访问,请改用带密码的反向代理。Caddy 站点块只需 4 行,而 caddy hash-password 会输出它所需的 bcrypt 哈希:
ollama.example.com {
basic_auth {
you $2a$14$replace_with_the_generated_hash
}
reverse_proxy 127.0.0.1:11434
}Caddy 会自行通过 TLS(传输层安全)获取证书,因此流量会被加密。请先测试客户端:许多与 Ollama 通信的工具没有用于设置 Authorization 请求头的字段,使用基本身份验证时会因返回空的 401 Unauthorized 而失败。SSH 隧道不存在这一问题,因此这里默认推荐使用 SSH 隧道。
拉取模型并检查完整路径
podman exec -it ollama ollama pull gemma3:4b
curl -s http://127.0.0.1:11434/api/tags
curl -s http://127.0.0.1:11434/api/generate -d '{"model":"gemma3:4b","prompt":"Reply with the single word: ready","stream":false}'
du -sh ~/ollama-data/models/api/tags 返回列出 gemma3:4b 的 JSON。暂停片刻,等待权重从磁盘加载后,/api/generate 返回一个包含 response 字段的 JSON 对象。du 应报告一个接近已公布下载大小的数值。然后验证本指南所讨论的完整流程:
sudo reboot
# reconnect, then:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user is-active ollama.serviceactive 表示模型仍驻留在内存中,[Install] 部分和 daemon-reload 都已正常工作。inactive 表示三者中有一项缺失。
故障模式及对应的提示信息
重启后容器消失。 首先检查 loginctl show-user ollama --property=Linger,因为没有 Linger=yes,用户的 systemd 实例就不会在启动时启动。如果已启用 lingering,请确认 .container 文件中存在 [Install] 段;如果修改过该文件,还必须运行 systemctl --user daemon-reload。
Error: statfs /home/ollama/ollama-data: no such file or directory。 容器启动前,绑定挂载的源路径必须存在。Podman 不会自动为您创建主机目录。以 ollama 用户身份运行 mkdir -p ~/ollama-data。
启动在 90 秒时失败。 journalctl --user -u ollama.service 显示 Start operation timed out. Terminating.,因为此时镜像拉取仍在进行。手动拉取镜像,或保留 TimeoutStartSec=900。
容器启动后退出。 结合查看 podman logs ollama 和 sudo ausearch -m avc -ts recent,即可判断问题是否与 SELinux 标签有关。若 AVC 中出现 container_t 和 user_home_t,说明缺少 :Z。
从主机发出的请求被拒绝。 对服务 active 执行 curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused 通常表示 OLLAMA_HOST 在容器内被设置为环回地址。删除该行。
生成速度很慢,或容器被终止。 没有 GPU 时,推理会使用 CPU;大型模型本身就会运行缓慢。如果容器在请求处理中退出,且日志中包含 signal: killed,说明触发了内核的内存不足终止机制。请从上方图表中选择较小的标签。
更新固定版本的镜像
固定版本意味着更新由您主动执行,而不是被动发生。编辑 Image= 中的 ollama.container,然后重新加载并重启:
systemctl --user daemon-reload
systemctl --user restart ollama.service
podman exec ollama ollama --version模型位于绑定挂载中,因此更换镜像不会影响模型。[Container] 部分中的 AutoUpdate=registry 适用于使用浮动标签的用户;对于固定版本标签,它没有实际作用,因为该标签指向的内容不会变化。备份 /home/ollama/ollama-data/models/manifests 和 .container 文件,但跳过 blobs:它们体积较大,ollama pull 会在新主机上重新获取这些文件。
FAQ
为什么 rootless Podman 容器会在我注销后停止?
用户的 systemd 实例及其 /run/user/<uid> 目录会在该用户的最后一个会话结束时被销毁,所有 rootless 容器也会随之停止。运行 sudo loginctl enable-linger ollama,确认 loginctl show-user ollama --property=Linger 输出 Linger=yes。创建 Quadlet 单元前启用 lingering,因为该单元所需的运行时目录只有在启用 lingering 后才会存在。
Ollama 模型目录需要 SELinux 标签吗?
在 Fedora、RHEL、Rocky 和 AlmaLinux 上,如果绑定挂载主机目录,则需要。容器运行在 container_t 域中,而主目录下的目录标签为 user_home_t,因此写入会被拒绝,Ollama 随即退出。将 :Z 追加到 Volume= 行,并为其指定专用子目录,因为重新标记会递归执行;如果将 :Z 指向整个主目录,会导致该用户无法访问 SSH 密钥。Podman 会为命名卷正确设置标签,无需额外操作。
Ollama 模型需要多少磁盘空间?
先参考 ollama.com/library 发布的下载大小,范围从 3.3 GB(gemma3:4b)到 19 GB(qwen3:30b)。在此基础上再加上 Podman 镜像的空间,并预留余量,因为第二个模型不会替换磁盘上的第一个模型。拉取前检查 df -h /home,拉取后检查 du -sh ~/ollama-data/models。RAM 也应按同样方式规划:模型加载时大约需要与其文件大小相当的内存,此外还要为上下文窗口预留空间。
在 VPS 上暴露端口 11434 安全吗?
不安全。Ollama 没有任何身份验证机制,因此任何能访问该端口的人都可以列出模型、删除模型、将新模型拉取到磁盘,并占用 CPU 和带宽配额运行推理。通过互联网传输的普通 HTTP 还会以明文发送所有提示词和生成结果。使用 PublishPort=127.0.0.1:11434:11434 将主机侧绑定到 127.0.0.1,通过 ss -ltnp | grep 11434 确认,然后通过 SSH 隧道或要求密码的反向代理访问。