VPS 上用 rootless Podman 运行 Ollama
了解如何在 VPS 上安全运行 Ollama:使用专用用户、lingering 和 Quadlet 实现重启后自动恢复,配置 SELinux 标签,并仅通过 SSH 隧道访问 11434 端口。
在 VPS 上以 rootless Podman 运行 Ollama
要在服务器上以 rootless Podman 运行 Ollama,必须满足 5 个条件,而桌面环境的操作指南可以跳过这些步骤。容器由专用的非特权用户负责。该用户启用了 lingering,因此退出登录后容器仍会继续运行。Quadlet 文件将容器交给 systemd 管理,因此重启后容器会自动恢复。对于强制执行 SELinux 的发行版,模型目录必须带有 SELinux 标签。API 仅监听回环地址,并通过 SSH(secure shell)隧道访问。
Ollama 是用于运行大型语言模型(LLM)的服务器。它将模型权重存储在磁盘上,将其加载到内存,并在端口 11434 上响应 HTTP 请求。它没有登录功能、API 密钥或用户账户,因此网络是唯一的访问控制。Podman 无需 daemon 且无需 root 即可运行容器,因此任何从容器中逃逸的进程,最初都只是普通的非特权用户进程。如果您希望先了解运行时对比,请阅读 Podman 和 Docker 在 VPS 上的区别。如果您想完全跳过容器,直接在 VPS 上安装 Ollama 的步骤更少。
SSD Nodes 在其镜像中提供 Fedora,而 Fedora 默认同时提供 Podman 和 SELinux(security-enhanced Linux)。以下每条命令都适用于安装了 Podman 5 或更高版本的发行版。
服务器需要调整笔记本配置的原因
Fedora Magazine 于 2026 年 8 月 5 日发布了一篇清晰介绍此技术栈的教程:在 Fedora Linux 上使用 Podman 本地运行 Ollama,作者是 Yazan Monshed。这是熟悉这些工具的一个很好的入门教程。但它面向笔记本电脑,其中 4 项配置在具有公网 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 应输出两行,分别来自这两个文件,每行都应包含一个有 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 下。
在拉取任何内容前,先规划磁盘空间。已发布的下载大小是最低需求。
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使用已发布的版本标签,截至 2026 年 8 月应为 0.32.9,不要使用 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 将主机侧绑定到 loopback。请确认这一点:
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 绑定到容器自身的 loopback,而 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,[Install] 部分缺失于 .container 文件中,或者您编辑文件后未运行 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,说明触发了内核的 out-of-memory killer。请从上方表格中选择更小的标签。
更新固定版本镜像
固定版本意味着更新由您主动执行,而不是被动接受。编辑 Image= 中的 ollama.container,然后重新加载并重启:
systemctl --user daemon-reload
systemctl --user restart ollama.service
podman exec ollama ollama --version模型存储在绑定挂载中,因此更换镜像不会影响模型。对于使用浮动标签的用户,AutoUpdate=registry 位于 [Container] 部分;但如果使用固定版本标签,该设置没有实际作用,因为该标签指向的内容不会变化。备份 /home/ollama/ollama-data/models/manifests 和 .container 文件,跳过 blob:它们体积很大,ollama pull 会在新主机上重新获取这些文件。
FAQ
为什么我退出登录后,无 root 权限的 Podman 容器会停止?
用户的 systemd 实例及其 /run/user/<uid> 目录会在该用户的最后一个会话结束时被拆除,所有无 root 权限的容器也会随之停止。运行 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 也会退出。在 Volume= 行追加 :Z,并为其指定专用子目录,因为重新标记会递归执行,将 :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。内存也应按相同方式规划:模型加载后,大约需要与其文件大小相当的内存,另加上下文窗口所需的内存。
在 VPS 上暴露 11434 端口安全吗?
不安全。Ollama 未提供任何身份验证,因此任何能够访问该端口的人都可以列出和删除模型,将新模型拉取到磁盘,并占用 CPU 和网络流量执行推理。通过互联网传输的普通 HTTP 还会以明文发送所有提示词和生成结果。使用 PublishPort=127.0.0.1:11434:11434 将主机端绑定到 127.0.0.1,通过 ss -ltnp | grep 11434 确认,然后使用 SSH 隧道访问,或通过要求密码的反向代理访问。