SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-13

Langfuse 自架部署:VPS 資源、TLS 與備份

在自己的 VPS 執行 Langfuse,了解實際最低資源、固定 image tag、TLS 設定,以及 ClickHouse retention 和可還原的備份方式,避免磁碟填滿。

為什麼要追蹤 AI agent

自行架設 Langfuse,可查看 agent 在一次執行期間實際做了什麼。Langfuse 是開放原始碼的 LLM(大型語言模型)可觀測性工具。它會記錄每個提示、每個模型回應、每次工具呼叫及每個 token,然後將這些資料歸入一個可開啟和閱讀的 trace。在自己的 VPS 上執行 Langfuse,這些提示就不會離開你控制的伺服器。

需要這麼做的原因很直接。看不見成本問題或品質問題,就無法修正。供應商帳單只能告訴你,星期二的費用是星期一的 4 倍。trace 則會告訴你是哪次 agent 執行造成這個結果、哪個提示增加到 40,000 個 token,以及哪個重試迴圈在放棄前執行了 9 次。帳單提供數字,trace 則提供產生這個數字的程式執行脈絡。

本指南會使用 3 個術語。trace 是 agent 的一次端到端執行。observation 是該次執行中的一個步驟:一般程式碼使用 span,呼叫模型則使用 generation。score 是附加在 trace 上的數值,來源可以是人工審查或自動評估器。Langfuse 支援 OpenTelemetry(OTel),這是與供應商無關的分散式追蹤標準,因此你現有的 instrumentation 可以指向 Langfuse。

實際自架 Langfuse 會執行哪些元件

Langfuse v4 不只是一個容器。它由 2 個應用程式容器和 4 個儲存服務組成。若部署在單一 VPS 上,這 6 個元件都會在你的主機上執行。

  • langfuse-web 提供 Web 介面和 ingestion API。
  • langfuse-worker 在背景處理佇列。它會解析 ingestion 批次、計算成本,並執行每晚的保留工作。
  • Postgres 儲存使用者、組織、專案、API keys 和 prompts 等交易資料。
  • ClickHouse 儲存 trace 資料本身,也就是 observations 和 scores。它是針對分析查詢設計的 column store,因此即使 dashboard 查詢超過 100 million rows,仍能快速回應。
  • Redis 是位於 web 和 worker 之間的佇列與快取。
  • MinIO 在主機上提供 S3 相容的物件儲存。它會儲存所有原始傳入事件,以及你附加的任何媒體檔案。

Langfuse 會針對執行主要工作的 3 個元件公布最低資源需求。

ChartLangfuse published minimum resources per component
The data behind this chart
[
  {
    "label": "ClickHouse",
    "cpu_cores": 2,
    "memory_gib": 8
  },
  {
    "label": "Langfuse web",
    "cpu_cores": 2,
    "memory_gib": 4
  },
  {
    "label": "Langfuse worker",
    "cpu_cores": 2,
    "memory_gib": 4
  }
]

僅 ClickHouse 就需要 8 GiB 記憶體。Web 容器和 worker 各需要 4 GiB。這些是 Langfuse 所估算的 3 個元件之最低需求,Postgres、Redis 和 MinIO 仍需額外記憶體。專案自己的 Docker Compose 指南建議使用 4 核心、16 GiB 記憶體和約 100 GiB 儲存空間的主機。這個建議符合上述計算結果,並非刻意提高規格。

不要在 2 GiB 方案上嘗試。ClickHouse 會先啟動並接受一段時間的寫入,之後在背景合併期間停止,因為合併會將資料表的大型 parts 載入記憶體。你會看到 docker compose ps 將 clickhouse 容器回報為 restartingdmesg 顯示類似 Out of memory: Killed process 1234 (clickhouse-serv) 的訊息,而所有 Langfuse dashboard 都回傳 500。在負載較低時,ClickHouse 也可能直接拒絕查詢,並記錄 DB::Exception: Memory limit (total) exceeded。8 GiB 足以支援每天傳送幾千筆 trace 的單一開發者。規劃時應以 16 GiB 為目標。

使用 Docker Compose 部署 Langfuse

複製 repository。stack、服務連線設定與預設環境變數都位於其中的 docker-compose.yml

git clone https://github.com/langfuse/langfuse.git
cd langfuse

該檔案中所有必須修改的值都標示為 # CHANGEME。先產生 3 個應用程式 secret。

openssl rand -base64 32   # NEXTAUTH_SECRET
openssl rand -base64 32   # SALT
openssl rand -hex 32      # ENCRYPTION_KEY

ENCRYPTION_KEY 必須是以 64 個十六進位字元表示的 256 位元值,這正是 openssl rand -hex 32 的輸出格式。它會加密靜態儲存的敏感值,包括儲存在該 instance 中的 LLM provider key。資料建立後再修改此值,這些資料列將無法解密,因此從第一次啟動起就應視為永久值。SALT 用於雜湊 Langfuse API key,因此修改後,agent 目前使用的所有 key 都會失效。

接著設定 POSTGRES_PASSWORDCLICKHOUSE_PASSWORDREDIS_AUTHMINIO_ROOT_PASSWORD。MinIO 密碼會出現在 4 個位置:第 1 次是 MINIO_ROOT_PASSWORD,之後還會出現在 LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEYLANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEYLANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY。漏掉任何一處,MinIO 都會以 SignatureDoesNotMatch 拒絕該用戶端;此錯誤會寫入 worker log,但 Web 介面仍可能看起來正常。將這些值放在 env 檔案中,而不是已納入版本控制的 compose 檔案中,這就是 Docker Compose env 檔案與 secret 所介紹的做法。

開始前固定 image tag

隨附檔案使用 langfuse/langfuse:4langfuse/langfuse-worker:4。這些 tag 會變動。Langfuse 會在啟動時自動執行 Postgres 和 ClickHouse migration,因此數個月後例行執行 docker compose pull,可能變成未預期的 schema migration,而且你當天並未備份資料庫。請在 docker-compose.override.yml 中將兩者固定為同一個 release;Compose 會將其合併並覆寫隨附檔案,因此之後執行 git pull 時不會覆蓋你的修改。

services:
  langfuse-web:
    image: docker.io/langfuse/langfuse:4.3.1
  langfuse-worker:
    image: docker.io/langfuse/langfuse-worker:4.3.1

截至 August 2026,Version 4.3.1 是目前的 4.3 release(之後已發布 4.4.0)。請查看專案的 GitHub releases 頁面,固定為部署當日的目前版本,之後再有計畫地更新該版本號。隨附檔案中的儲存服務 image 已固定到 major version,分別是 postgres:17clickhouse-server:25.12redis:7,也應採用相同做法。

啟動 stack。

docker compose up -d
docker compose ps
docker compose logs -f langfuse-worker

第一次啟動會執行 migration,因此請等待 1 到 2 分鐘後再測試服務。docker compose ps 應列出 6 個處於 running 狀態的服務。如果 worker 不斷重新啟動,請查看其 log 以取得原因:CLICKHOUSE_MIGRATION_URL 使用 port 9000 的 ClickHouse native protocol,而不是 HTTP port 8123;若指向 8123,worker 會在此失敗,但 web container 仍可能看起來正常。

從該主機本身檢查健康狀態。

curl -s "http://localhost:3000/api/public/health?failIfDatabaseUnavailable=true"
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/api/public/ready

單純執行 /api/public/health 只能證明 API process 正在運作,因為它刻意略過資料庫,讓 Postgres 短暫異常時服務仍能提供回應。failIfDatabaseUnavailable=true 形式才適合交給監控系統;資料庫無法連線時,它會回傳 503。migration 完成後,/api/public/ready 會回傳 200,表示 container 可以接受流量。兩者都是一般的 HTTP 檢查,因此可以讓 Uptime Kuma 狀態頁面監控它們,並在 agent 發現異常前告知你 stack 已停止服務。

在前方加入 TLS 並關閉額外連接埠

隨附的 Compose 檔案會為 Web 容器發布 3000:3000,並為 MinIO 發布 9090:9000。兩者都會繫結到所有網路介面。在公開 IP 上,任何掃描 3000 埠的人都能開啟註冊頁面,任何掃描 9090 埠的人都能連到存放原始提示的 bucket。

僅設定防火牆規則並不會關閉這些連接埠。Docker 會將自己的 DNAT 規則寫入 nat table,而這些規則會在封包交由 ufw 的 filter 規則處理前先行套用,因此 ufw deny 3000 仍會讓發布的連接埠保持開放。這個問題相當常見,甚至有專門的指南說明:Docker 發布的連接埠為何會繞過 ufw。請在 override 檔案中改為繫結至 loopback。

services:
  langfuse-web:
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      NEXTAUTH_URL: https://langfuse.example.com
  minio:
    ports:
      - "127.0.0.1:9090:9000"
      - "127.0.0.1:9091:9001"

NEXTAUTH_URL 必須是包含 scheme 的完整公開位址,因為登入流程會從這個值建立 callback URL。若在 HTTPS proxy 後方仍保留 http://localhost:3000,登入往返流程會將瀏覽器導向無法連線的位置。

現在將 reverse proxy 指向 127.0.0.1:3000,並讓它負責憑證。通常會在同一個 Compose 專案中使用 Traefik,路由 labels 的設定方式請參考使用同一個 Traefik reverse proxy 執行多個應用程式。如果這台主機只執行 Langfuse,Caddy 也能用兩行設定完成相同工作。使用 curl -sI https://langfuse.example.com/api/public/ready 驗證,接著從另一台機器確認 curl http://YOUR_IP:3000 現在會逾時。

MinIO 有一項例外需要注意。Langfuse 會透過指向該 S3 endpoint 的 presigned URL,將附加媒體提供給瀏覽器。因此,如果使用包含影像或音訊的多模態 trace,僅繫結至 loopback 的 MinIO 會導致這些附件無法載入。設定 proxy 前,請先閱讀 blob storage 設定頁面,因為寫入 presigned URL 的 endpoint 必須與公開發布的 endpoint 相符。純文字 trace 不受影響。

首次造訪時建立帳戶,之後確保此 instance 由你管理。將 LANGFUSE_ALLOWED_ORGANIZATION_CREATORS 設為你自己的電子郵件地址,讓陌生人即使連到頁面,也無法在你的伺服器上建立 organization。如果你已經使用 Authentik 作為自己的 identity provider,Langfuse 可使用標準 OIDC 連線。如此一來,帳戶會隨其他應用程式一併管理,而不是只存在於這台主機知道的密碼清單中。

傳送你的第一筆 trace

在 Web 介面中建立專案,並從專案設定複製其 public key 和 secret key。Python SDK 會讀取 3 個環境變數。

export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_BASE_URL="https://langfuse.example.com"

LANGFUSE_BASE_URL 是 SDK v4 中的變數名稱。SDK v4 於 2026 年 3 月發布。較舊的程式碼和指南使用 LANGFUSE_HOST。如果你的 trace 被傳送到 Langfuse Cloud,而不是自己的伺服器,原因是 base URL 未設定,因為預設值會指向託管執行個體。

pip install langfuse opentelemetry-instrumentation-anthropic anthropic
import os
from anthropic import Anthropic
from langfuse import get_client, observe
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor

AnthropicInstrumentor().instrument()
langfuse = get_client()
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

@observe(as_type="tool")
def lookup_order(order_id: str) -> str:
    return f"order {order_id}: shipped"

@observe()
def handle_request(question: str) -> str:
    context = lookup_order("A-1042")
    message = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=512,
        messages=[{"role": "user", "content": f"{context}\n\n{question}"}],
    )
    return message.content[0].text

if __name__ == "__main__":
    assert langfuse.auth_check()
    print(handle_request("Where is my order?"))
    langfuse.flush()

@observe decorator 會在函式周圍建立 observation,擷取函式的引數和回傳值,並將其巢狀在目前已啟用的 observation 下。AnthropicInstrumentor 是 Anthropic client 的 OpenTelemetry instrumentation。它會將每個 messages.create 呼叫轉換為 generation,並記錄模型名稱、token 使用量和延遲。呼叫端不需要修改。

以下 2 個呼叫會替你完成檢查。langfuse.auth_check() 在 key 無效或 base URL 錯誤時會回傳 False。這比猜測 dashboard 為何沒有資料更快。langfuse.flush() 會等待佇列中的 span 傳送完成。短時間執行的程序需要這項操作,因為 SDK 會在背景批次傳送資料,而立即結束的 script 會連同尚未傳送的批次一併結束。

為什麼 ClickHouse 會持續成長?

Traces 是多數人自架時成長最快的資料。每次 agent 執行的每個步驟都會寫入一筆資料,而且輸入與輸出會完整儲存。因此,提示詞較長且互動頻繁的 agent,每天產生的位元組數遠高於它所監看的應用程式。若不加處理,ClickHouse 會填滿磁碟;磁碟滿載後會停止擷取資料,而不是單純變慢。

這裡有兩個不同的成長來源,必須分別處理。

第一個是你自己的 trace 資料,解法是設定保留期限。在 Web 介面開啟專案設定,並以天數設定資料保留期限。Langfuse 接受的最短期限為 3 天。之後,夜間工作會選取超過該期限的 traces、observations、scores 與媒體資產,並從 ClickHouse 及 blob storage 中刪除。該工作需要 bucket 上的 DeleteObject 權限;預設 compose file 中的 MinIO root credentials 已具備此權限。刪除作業無法復原,因此如果需要長期保存歷史資料,請先設定 blob storage export。不要自行在 Langfuse 的資料表上撰寫 TTL 子句:保留工作會讓 ClickHouse 與 bucket 保持同步,而手動 TTL 只會刪除其中一側。

請根據實際用途選擇保留期限。成本與品質檢查通常使用數天前的資料,而不是數月前的資料。對小型團隊而言,30 天是合理的起點;如果只有在發生問題時才查看 trace,14 天通常已足夠。

第二個來源是 ClickHouse 自己的 system log tables。這一點常讓人意外,因為即使設定了保留期限,磁碟仍會持續成長。ClickHouse 會為自身診斷寫入 trace_logtext_logopentelemetry_span_logmetric_logasynchronous_metric_log。這些資料表預設沒有 TTL,而且 Langfuse 從不讀取它們。請先找出磁碟空間實際消耗的位置。

SELECT table, formatReadableSize(size) AS size, rows FROM (
    SELECT table, database, sum(bytes) AS size, sum(rows) AS rows
    FROM system.parts
    WHERE active
    GROUP BY table, database
    ORDER BY size DESC
)

使用 docker compose exec clickhouse clickhouse-client --password "$CLICKHOUSE_PASSWORD" 執行。如果 system tables 接近清單頂端,請使用設定覆寫檔停用它們,因為 ClickHouse 啟動時會將 /etc/clickhouse-server/config.d/ 中的每個檔案合併到主要設定檔上。

<clickhouse>
    <trace_log remove="1"/>
    <text_log remove="1"/>
    <opentelemetry_span_log remove="1"/>
    <asynchronous_metric_log remove="1"/>
    <metric_log remove="1"/>
</clickhouse>

掛載後重新啟動 ClickHouse。

services:
  clickhouse:
    volumes:
      - ./clickhouse-config.d/system-logs.xml:/etc/clickhouse-server/config.d/system-logs.xml:ro

這會停止新的寫入。磁碟上已有的資料列仍會保留,因此請使用 DROP TABLE IF EXISTS system.trace_log 明確回收空間;對每個移除的資料表也執行相同操作。如果希望保留診斷資料,另一種做法是在每個資料表上設定積極的 TTL,而不是使用 remove="1";Langfuse scaling docs 對此有詳細說明。

還有一個資料表值得了解。blob_storage_file_log 追蹤上傳至 bucket 的事件檔案。如果你也在 bucket 上設定 lifecycle policy,請為該資料表設定相符的 TTL,避免兩者逐漸不同步。

ALTER TABLE blob_storage_file_log MODIFY TTL created_at + INTERVAL 30 DAY DELETE;

另外,請在資料磁碟上設定簡單的 df -h alert。Traces 不會平順地成長。你發布新 agent 的那一天,資料量可能突然增加;擷取資料失敗不應該是第一個徵兆。

備份 Postgres 與 ClickHouse

Langfuse 備份分為 3 個部分。Postgres 儲存使用者、組織、專案與 API keys。ClickHouse 儲存 traces。MinIO 儲存原始事件。只還原 Postgres,您會得到可正常登入的系統,但沒有歷史資料。只還原 ClickHouse,您會得到無人能登入查看的歷史資料。

Postgres 是一般的 pg_dump,這也是 Langfuse 備份文件建議的方法。

docker compose exec -T postgres pg_dump -U postgres postgres \
  | gzip > langfuse-pg-$(date +%F).sql.gz

ClickHouse 需要更謹慎處理,因為在 merge 執行期間複製正在使用的資料目錄,無法形成一致的備份。在單一主機上,簡單的方法是停止容器並封存 volume。

docker compose stop clickhouse
docker volume ls | grep clickhouse
docker run --rm -v langfuse_langfuse_clickhouse_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/langfuse-ch-$(date +%F).tar.gz -C /data .
docker compose start clickhouse

請使用 docker volume ls 顯示的 volume 名稱,不要使用 YAML 中寫入的名稱。檔案宣告了 langfuse_clickhouse_data,而 Compose 會在前面加上 project name,因此位於名為 langfuse 目錄中的副本會產生 langfuse_langfuse_clickhouse_data。如果名稱錯誤,docker run 會在不顯示錯誤的情況下建立新的空 volume,您的封存檔也會是空的。

Web 容器會先將每個收到的事件寫入 bucket,再由 worker 處理,因此短暫停止 ClickHouse 通常只會讓 worker 稍後重試。請在低流量時段執行,並將停止時間縮到最短。對於流量較大的執行個體,ClickHouse 內建的 BACKUP DATABASE default TO S3(...) 陳述式可以在不停止伺服器的情況下寫入一致的備份。MinIO 是第三個部分,使用 mc mirror 或將 MinIO 複寫至伺服器外的 bucket 即可涵蓋。無論採用哪種方式,都要將備份移出伺服器;這正是 VPS 上的加密 restic 備份 的用途。

Redis 不需要備份。它儲存 queue 與 cache,因此遺失 Redis 只會造成目前處理中的事件遺失,不會影響更早的資料。

一致性限制確實存在,應明確說明。Postgres 與 ClickHouse 會在不同時間點傾印,因此還原後可能出現沒有 traces 的 project row,也可能出現屬於已不存在 project 的 traces。Langfuse 可以容忍這種情況,但仍應在接近的時間點及低流量時段取得兩份傾印。事件 bucket 才是真正的安全網,因為 Langfuse 會在處理每個收到的事件前,先將其持久化到 bucket。

至少要在 scratch stack 中還原一次。如此一來,您可以現在發現錯誤的 volume 名稱,而不是等到服務中斷時才發現。

首先應查看的項目

以下4項值得在第一週優先檢查。

  • 每筆 trace 的成本。 Langfuse 會根據模型名稱與 token 用量計算成本,因此請依成本排序 trace,從頭到尾閱讀成本最高的一筆。問題通常出在不斷膨脹的 prompt:將整份文件貼入 context,或未刪減的對話歷史。看清楚問題後,控制 AI agent 的成本 就會成為工程工作,而不是猜測。
  • 依 input 與 output 拆分的 token 用量。 input token 數量多且便宜,output token 數量少但昂貴,而 cached input 更便宜。Claude Code 如何計算 token 用量 會進一步說明相同的計算方式;這也適用於自行撰寫的任何 agent。
  • 延遲百分位數。 中位數會掩蓋問題。p95 與 p99 是 timeout 發生的區間;在 agent loop 中,p95 的慢速 tool call 會隨迭代次數累積。
  • 失敗的 tool call。 依層級 ERROR 篩選 observation。某個 tool 若有 5% 的失敗率,在整體成功率中可能不明顯,但在 trace 中十分明顯;你會看到模型重試,接著消耗 token 來避開問題。

設定 retention window,並選定一個每週部署當天都要查看的 dashboard。沒有人開啟的 observability tool,最後只會成為填滿磁碟的資料庫。

FAQ

自架 Langfuse 需要多少記憶體?

請準備 4 個 CPU 核心與 16 GiB 記憶體。這是 Langfuse Docker Compose 指南對單一虛擬機器的建議配置,另需約 100 GiB 儲存空間。已發布的元件最低需求為 ClickHouse 的 8 GiB,以及 web 與 worker 容器各 4 GiB。Postgres、Redis 與 MinIO 仍需額外記憶體。8 GiB 可執行 1 名開發人員使用的實例。2 GiB 則不足:ClickHouse 在背景合併期間會被 kernel 終止,而 dmesg 會顯示 Out of memory: Killed process

為什麼設定資料保留期限後,ClickHouse 磁碟仍持續被填滿?

資料保留設定只涵蓋 Langfuse 自身的資料。ClickHouse 會另外寫入診斷資料表 trace_logtext_logopentelemetry_span_logmetric_logasynchronous_metric_log,而這些資料表預設沒有 TTL。依資料表彙總查詢 system.parts,找出佔用空間最大的資料表;接著在 /etc/clickhouse-server/config.d/ 下的檔案中加入 remove="1" 項目以停用未使用的資料表,重新啟動 ClickHouse,並刪除現有資料表,以回收已使用的空間。

Langfuse 的最低資料保留期限是多少?

3 天。您可以在專案設定中,或透過 projects API,依專案設定保留期限。每晚執行的工作會從 ClickHouse 與 blob storage 刪除超過期限的 traces、observations、scores 與 media assets。刪除後無法復原;如果需要保留超過該期限的歷史資料,請先設定 blob storage 匯出。

是否必須同時備份 Postgres 與 ClickHouse?

是,因為兩者儲存的資料不同。Postgres 儲存使用者、組織、專案與 API keys;ClickHouse 則儲存 trace 資料本身。只還原 Postgres 會得到一個可以登入、但沒有任何資料的實例。也請備份 MinIO bucket,因為其中儲存 Langfuse 收到資料時保存的原始事件;在這個堆疊中,這是最接近來源資料的內容。

可以將現有的 OpenTelemetry 設定指向自架 Langfuse 嗎?

可以。Langfuse v4 與其 v4 SDK 都建構於 OpenTelemetry 之上,Anthropic 與 OpenAI OTel instrumentations 可直接將資料匯出至 Langfuse。在 Python 中執行 pip install langfuse opentelemetry-instrumentation-anthropic,於啟動時呼叫一次 AnthropicInstrumentor().instrument(),並將 LANGFUSE_PUBLIC_KEYLANGFUSE_SECRET_KEYLANGFUSE_BASE_URL 設定為您自己的主機。先使用 langfuse.auth_check() 確認,再排查缺少的儀表板。