VPS 安全架設 Ollama,自架 LLM API
7B 模型約需 8 GB RAM,CPU 每秒可產生 4 到 10 個 token。用 VPS 架設 Ollama,透過 127.0.0.1:11434/v1 呼叫,並保持 port 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。因此,計算方式很簡單,而且會決定所有事項。
3B 模型(例如 llama3.2:3b)的下載大小約為 2 GB,執行時需要約 4 GB 可用 RAM。7B 或 8B 模型(例如 mistral:7b 或 llama3.1:8b)的磁碟大小約為 5 GB,需要約 8 GB RAM;若要順暢使用,建議有 16 GB。13B 或 14B 模型約需 16 GB。30B 到 70B 範圍內的模型需要大容量 RAM 的主機,或更實際地說,需要 GPU。在 CPU VPS 上,這類模型不是無法載入,就是回應速度慢到無法使用。
接著說明速度,因為這是許多人低估的部分。CPU 推論受記憶體頻寬限制,而不是時脈速度;共用 vCPU VPS 的記憶體頻寬通常有限。預期每秒只能產生個位數到低兩位數的 token:7-8B Q4 模型可能達到每秒 4 到 10 個 token,3B 模型則約為每秒 10 到 25 個 token。GPU 的速度大約快一個數量級。這些數字刻意只提供大致範圍;正確做法是測量你自己的主機,以下的執行步驟會示範如何進行。請相信你的 eval rate,不要相信任何文章中的數字,包括本文。
實際結論是:如果能接受這樣的速度,在 CPU 上執行小型量化模型,確實適合用於草稿、摘要與分類。若需要更大型或更快速的模型,請將 GPU 執行個體的成本納入預算。
若要比較特定模型與特定主機的需求,請在此估算其記憶體占用量:
安裝 Ollama
有兩種正確的方式。在純 VPS 上,官方 script 最簡單:
curl -fsSL https://ollama.com/install.sh | sh這會建立名為 ollama 的 system user,將 binary 安裝至 /usr/local/bin/ollama,並註冊名為 ollama.service 的 systemd service。該 service 會在開機時啟動,並繫結至 127.0.0.1:11434。確認服務已啟動:
systemctl status ollama
ollama --version如果已經使用 Docker,也可以改用 container:
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,則會在所有介面上公開連接埠,這正是安全性章節警告的錯誤設定。請選擇其中一種安裝方式。不要同時執行 script 和 container,否則兩個程序會爭用同一個連接埠。
拉取並執行第一個模型
ollama pull llama3.2:3b
ollama run llama3.2:3bpull 會將模型層下載到磁碟(此模型約 2 GB)。run 會將這些模型層載入記憶體,並顯示 >>> 提示字元。輸入問題。載入權重並從磁碟讀入 RAM 時,產生第一個 token 可能需要數秒,之後答案會以串流方式輸出。輸入 /bye 離開聊天;Ollama 會繼續在背景執行。
查看已載入的內容,以及其配置方式:
ollama psPROCESSOR 欄會顯示實際狀態。100% CPU 表示未使用 GPU,速度緩慢的原因就在這裡。使用 verbose flag 測量實際速度:
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 的 volume。這點很重要,因為量化權重很快就會累積: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。systemctl edit 會重新載入 unit 檔案,因此請重新啟動服務以套用變更:
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,並要求密碼或 token,再將請求代理至
127.0.0.1:11434。Ollama 維持繫結至 localhost;公開埠只由反向代理監聽。這與在任何本機服務前方的 nginx 上設定 Let's Encrypt 憑證相同。
接下來的聊天 UI 會採用反向代理方式,並加入真正的登入功能。
使用 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 旗標是關鍵設定。它會讓容器使用主機的 network namespace,因此容器內的 127.0.0.1 就是主機自己的 loopback,容器可透過 127.0.0.1:11434 連線到 Ollama,而不必讓 Ollama 監聽其他介面。其他地方會看到的 bridge network 設定(--add-host=host.docker.internal:host-gateway 搭配 OLLAMA_BASE_URL=http://host.docker.internal:11434)在此無法運作:該名稱會解析為 Docker bridge gateway,而主機上繫結至 127.0.0.1 的服務無法跨 bridge 存取,因此 Open WebUI 只會持續顯示無法連線到 Ollama。
使用 host networking 的代價是,Open WebUI 現在會在主機的所有介面上監聽 8080;任何 -p 對應都會被捨棄,Docker 也會顯示警告。因此,請在主機與供應商防火牆上都封鎖 8080,並讓 TLS reverse proxy 成為唯一公開入口。首次造訪時,Open WebUI 會要求建立管理員帳戶。該帳戶就是驗證層,請設定強密碼。
若要從筆記型電腦透過 HTTPS 開啟聊天介面,請在 127.0.0.1:8080 前方配置 TLS reverse proxy。如果這台主機上已經透過路由處理多個 Docker 應用程式,使用自動為多個應用程式配置 TLS 的 Traefik 是最合適的選擇:只需一個 label 區塊,就能簽發憑證並將 chat.example.com 路由至 Open WebUI。安全性章節中的規則仍然適用:proxy 負責公開連接埠與登入,而 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 必須是已經 pull 的模型名稱;使用未知名稱時會回傳 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。Linux OOM killer 會停止該程序,因為模型需要的 RAM 超過伺服器可提供的容量。使用 sudo dmesg | grep -i oom 確認原因,並查看類似 Out of memory: Killed process ... (ollama) 的行。解決方式是改用較小或量化程度更高的模型,例如使用 llama3.2:3b 取代 13B,或增加 swap。如此一來,僅稍微超出實體 RAM 的工作負載可以慢慢完成,而不會直接終止。swap 會把立即崩潰變成緩慢回應;它不會讓 4 GB 記憶體實際適合執行 70B 模型。除非你剛好正在監看終端機,否則程序遭終止時不會有明顯提示。因此,若你是從其他位置查詢該伺服器,可將 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 檢查),縮短 context length,或改用容量較大的 VPS。沒有任何 flag 能讓模型憑空符合容量限制,所需記憶體是實際存在的。
第一個 token 需要很久,之後就正常。 冷啟動的模型會在五到三十秒內完全沒有輸出,接著便正常串流。這段等待時間是因為模型首次將權重從磁碟載入 RAM,儲存裝置速度較慢時情況會更明顯。模型載入後,會在 OLLAMA_KEEP_ALIVE 的持續時間內保留在記憶體中,因此第二次提示會立即得到回應。如果第一次載入時間超過呼叫路徑中某處的逾時值,收到的會是錯誤,而不是較慢的回應;透過 確認哪一層回報 context deadline exceeded,即可判斷是用戶端、代理程式還是模型載入本身逾時。若等待間隔造成困擾,請提高該值,並使用 ollama ps 查看目前是否已有模型載入。
所有內容都很慢。 每秒輸出 10 個 token 或更少,但完全沒有錯誤。這表示 CPU inference 正按照 CPU inference 的特性運作。ollama ps 會顯示 100% CPU,表示沒有 GPU。這不是錯誤,也沒有任何設定可以修正,因為限制因素是記憶體頻寬,而不是設定錯誤。改用較小的模型、接受這個速度,或改用 GPU instance;在判定任何問題發生前,先使用 --verbose 測量實際速度。
從另一台機器連線遭拒。 從筆電連線時收到 curl: (7) Failed to connect to <ip> port 11434: Connection refused。這是預期行為:Ollama 只繫結 localhost。不要將其繫結至 0.0.0.0 來「修正」問題,因為這正是前述的暴露錯誤。請透過 VPN 或具備驗證功能的 proxy 存取模型。
你已將 11434 暴露至網際網路。 如果設定了 OLLAMA_HOST=0.0.0.0、開放防火牆,現在又看到從未啟動的模型拉取作業,或 CPU 被不明用戶端占用至 100%,表示主機已被發現並遭到利用。這是主要錯誤,不是罕見例外。重新繫結至 127.0.0.1 或 VPN 位址,在防火牆封鎖 11434,並在前端加入驗證。假設該位址開放期間,任何能連線到該位址的人都曾查詢過服務。
備份與升級
需要保留的狀態很少。模型可以重新下載,因此值得備份的只有 Open WebUI 的資料 volume、帳戶、聊天記錄、設定,以及你建立的任何 systemd drop-in。使用一次性容器備份該 volume:
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,接著重新建立容器。不要長期固定版本:模型品質與 runtime 的變化都很快,因此請閱讀 release notes,並在自己的主機上重新進行基準測試,不要直接採信上季的數據。
FAQ
我真的可以在僅使用 CPU 的 VPS 上執行 LLM 嗎?
可以,但有一些限制。3B 到 8B 的小型量化模型可以在 CPU 上執行,適合用於草擬、摘要和分類,只是速度較慢;在共用 vCPU 上,每秒通常只有個位數到十幾個 token。13B 以上的模型會慢得難以使用,或根本無法裝入 RAM。若需要更快的速度或更大型的模型,就需要 GPU instance。
每個模型需要多少 RAM?
以預設的 4-bit 量化模型而言,可以使用以下粗略規則:權重每 1 billion parameters 約需 0.5 GB RAM,另外需要約 1 GB 的額外記憶體,以及略多的 context 空間。因此,3B 模型約需 4 GB 可用記憶體,7-8B 模型約需 8 GB,14B 模型約需 16 GB。使用 free -h 檢查剩餘記憶體,並為作業系統及伺服器上的其他程式保留空間。
Ollama API 有驗證機制嗎?
沒有。Ollama 沒有內建驗證、API key 或速率限制。任何能連到 port 11434 的人都能完全控制它。這正是 Ollama 預設繫結至 127.0.0.1 的原因,也是你絕對不能將 11434 透過 0.0.0.0 暴露到網際網路的原因。請在本機、透過私有 VPN,或經由會加入登入機制的反向代理存取它。
如何加入網頁聊天介面?
在 Docker 中使用 --network=host 執行 Open WebUI,讓它共用主機的 loopback,並連線到原生 Ollama 的 http://127.0.0.1:11434;接著在其 port 8080 前方設定 TLS 反向代理,供筆電存取。請在防火牆上關閉 8080,讓反向代理成為唯一公開入口。Open WebUI 自己的管理員帳號會提供登入機制,並可在首次啟動時設定密碼。
如何從自己的應用程式呼叫它?
使用 http://127.0.0.1:11434/v1 的 OpenAI 相容端點。將任何 OpenAI SDK 的 base URL 指向該 URL。API key 可填入任意字串,因為系統會忽略它。將 model 設為你已拉取的模型名稱。現有的 OpenAI 程式碼通常不需修改即可執行,只需變更 base URL 和 key。