SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-13

วิธี Self-host Langfuse บน VPS เพื่อติดตาม AI Agents

เรียนรู้วิธีติดตั้ง Langfuse บน VPS ของคุณเอง พร้อมตั้งค่า TLS, การจัดการ ClickHouse เพื่อป้องกันดิสก์เต็ม, การใช้ Image Tags ที่เสถียร และแนวทางการสำรองข้อมูลที่ใช้งานได้จริง

เหตุผลที่ต้องทำ Trace ให้กับ AI agent

คุณสามารถ self-host Langfuse เพื่อดูว่า agent ของคุณทำงานจริงอย่างไรในแต่ละรอบ Langfuse เป็นเครื่องมือ observability สำหรับ LLM (large language model) แบบ open source โดยจะบันทึกทุก prompt, ทุกการตอบกลับจากโมเดล, ทุกการเรียกใช้ tool และทุก token จากนั้นจะจัดกลุ่มข้อมูลเหล่านั้นไว้ภายใต้ trace เดียวที่คุณสามารถเปิดอ่านได้ การรันเครื่องมือนี้บน VPS ของคุณเองหมายความว่า prompt เหล่านั้นจะไม่ถูกส่งออกจากเซิร์ฟเวอร์ที่คุณควบคุม

เหตุผลที่ควรทำเรื่องนี้มีความชัดเจน คุณไม่สามารถแก้ไขปัญหาด้านต้นทุนหรือคุณภาพที่คุณมองไม่เห็นได้ ใบแจ้งหนี้จากผู้ให้บริการบอกคุณได้เพียงว่าวันอังคารมีค่าใช้จ่ายสูงกว่าวันจันทร์ถึง 4 เท่า แต่ trace จะบอกคุณว่า agent รอบไหนที่เป็นต้นเหตุ, prompt ใดที่ขยายตัวจนมีขนาดถึง 40,000 tokens และ loop การลองใหม่ (retry loop) รอบใดที่ทำงานซ้ำถึง 9 ครั้งก่อนจะยอมแพ้ ใบแจ้งหนี้ให้ตัวเลขแก่คุณ แต่ trace ให้โค้ดที่เป็นต้นกำเนิดของตัวเลขนั้น

มีคำศัพท์ 3 คำที่ใช้ตลอดคู่มือนี้ trace คือการทำงานตั้งแต่ต้นจนจบของ agent หนึ่งรอบ observation คือขั้นตอนหนึ่งภายในรอบการทำงานนั้น ได้แก่ span สำหรับโค้ดทั่วไป และ generation สำหรับการเรียกใช้งานโมเดล score คือตัวเลขที่แนบไปกับ trace ซึ่งได้จากการตรวจสอบโดยมนุษย์หรือตัวประเมินอัตโนมัติ Langfuse รองรับ OpenTelemetry (OTel) ซึ่งเป็นมาตรฐานกลางสำหรับ distributed tracing ดังนั้น instrumentation ที่คุณมีอยู่แล้วจึงสามารถเชื่อมต่อกับเครื่องมือนี้ได้

การทำงานจริงของ Langfuse แบบ self-hosting

Langfuse v4 ไม่ได้มีเพียง container เดียว แต่ประกอบด้วย container ของแอปพลิเคชัน 2 รายการ และบริการจัดเก็บข้อมูล 4 รายการ ซึ่งหากรันบน VPS เครื่องเดียว ทั้ง 6 รายการจะทำงานอยู่บนเครื่องของคุณทั้งหมด

  • langfuse-web ทำหน้าที่ให้บริการเว็บอินเทอร์เฟซและ ingestion API
  • langfuse-worker ทำหน้าที่จัดการคิวในเบื้องหลัง โดยจะประมวลผล ingestion batches คำนวณต้นทุน และรันงาน retention ประจำวัน
  • Postgres จัดเก็บข้อมูลธุรกรรม เช่น ผู้ใช้ องค์กร โปรเจกต์ API keys และ prompts
  • ClickHouse จัดเก็บข้อมูล trace ซึ่งได้แก่ observations และ scores โดยเป็น column store ที่ออกแบบมาเพื่อการสืบค้นเชิงวิเคราะห์ ทำให้แดชบอร์ดสามารถแสดงผลข้อมูลนับร้อยล้านแถวได้อย่างรวดเร็ว
  • Redis ทำหน้าที่เป็นคิวและแคชที่อยู่ระหว่างเว็บและ worker
  • MinIO ให้บริการ object storage ที่รองรับ 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 ส่วน container เว็บและ worker ต้องการรายการละ 4 GiB นี่คือค่าขั้นต่ำที่ระบุไว้สำหรับส่วนประกอบ 3 รายการที่ Langfuse กำหนดขนาดไว้ และ Postgres, Redis รวมถึง MinIO ยังคงต้องการหน่วยความจำเพิ่มเติมอีก คู่มือ Docker Compose ของโปรเจกต์แนะนำให้ใช้เครื่องที่มี 4 คอร์, หน่วยความจำ 16 GiB และพื้นที่จัดเก็บข้อมูลประมาณ 100 GiB ซึ่งสอดคล้องกับการคำนวณนี้มากกว่าการเผื่อทรัพยากรไว้เกินความจำเป็น

อย่าพยายามรันบนแผนบริการขนาด 2 GiB เพราะ ClickHouse จะเริ่มทำงานและรับข้อมูลได้ช่วงหนึ่ง จากนั้นจะหยุดทำงานระหว่างการทำ background merge เนื่องจากกระบวนการ merge ต้องโหลดข้อมูลส่วนใหญ่ของตารางลงในหน่วยความจำ คุณจะพบว่า docker compose ps รายงานสถานะ container ของ clickhouse เป็น restarting โดยมี dmesg แสดงข้อความเช่น Out of memory: Killed process 1234 (clickhouse-serv) และแดชบอร์ด Langfuse ทุกรายการจะตอบกลับด้วยรหัส 500 หากภาระงานเบาลง ClickHouse จะปฏิเสธการสืบค้นแทนและบันทึก log ว่า DB::Exception: Memory limit (total) exceeded สำหรับนักพัฒนาคนเดียวที่ส่ง trace วันละไม่กี่พันรายการ หน่วยความจำ 8 GiB อาจพอใช้งานได้ แต่ควรวางแผนไว้ที่ 16 GiB จะเหมาะสมที่สุด

การติดตั้ง Langfuse ด้วย Docker Compose

โคลน repository ลงมา โดย stack, การเชื่อมต่อ และ environment เริ่มต้นทั้งหมดจะอยู่ใน docker-compose.yml ของโปรเจกต์

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

ค่าทุกค่าที่คุณต้องแก้ไขจะถูกระบุไว้ด้วย # CHANGEME ในไฟล์ดังกล่าว ให้สร้าง secret สำหรับแอปพลิเคชันทั้งสามรายการก่อน

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

ENCRYPTION_KEY ต้องมีความยาว 256 บิตในรูปแบบเลขฐานสิบหก 64 ตัวอักษร ซึ่งเป็นสิ่งที่ openssl rand -hex 32 แสดงผลออกมา ค่านี้ใช้สำหรับเข้ารหัสข้อมูลสำคัญที่จัดเก็บไว้ รวมถึงคีย์ของผู้ให้บริการ LLM ที่คุณเก็บไว้ใน instance ห้ามเปลี่ยนค่านี้หลังจากที่มีข้อมูลแล้ว เพราะจะทำให้ไม่สามารถถอดรหัสข้อมูลเดิมได้ ดังนั้นให้ถือว่าเป็นค่าถาวรตั้งแต่การบูตครั้งแรก ส่วน SALT ใช้สำหรับทำ hash ให้กับ Langfuse API keys ของคุณ การเปลี่ยนค่านี้จะทำให้คีย์ทั้งหมดที่ agent ของคุณใช้งานอยู่ใช้งานไม่ได้ทันที

จากนั้นตั้งค่า POSTGRES_PASSWORD, CLICKHOUSE_PASSWORD, REDIS_AUTH และ MINIO_ROOT_PASSWORD รหัสผ่านของ MinIO จะปรากฏอยู่ในสี่ตำแหน่ง ได้แก่ MINIO_ROOT_PASSWORD, LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY, LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY และ LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY หากพลาดไปตำแหน่งใดตำแหน่งหนึ่ง MinIO จะปฏิเสธการเชื่อมต่อของ client ด้วยข้อผิดพลาด SignatureDoesNotMatch ซึ่งจะปรากฏใน log ของ worker ในขณะที่หน้าเว็บอินเทอร์เฟซยังคงดูเหมือนทำงานปกติ การเก็บค่าเหล่านี้ไว้ในไฟล์ env แทนที่จะเก็บไว้ในไฟล์ compose ที่ถูก track อยู่เป็นรูปแบบที่อธิบายไว้ใน ไฟล์ env และ secrets ของ Docker Compose

กำหนดเวอร์ชัน image ให้คงที่ก่อนเริ่มใช้งาน

ไฟล์ที่ให้มาใช้ tag เป็น langfuse/langfuse:4 และ langfuse/langfuse-worker:4 ซึ่ง tag เหล่านี้มีการเปลี่ยนแปลงอยู่เสมอ Langfuse จะรันการ migration ของ Postgres และ ClickHouse โดยอัตโนมัติเมื่อเริ่มทำงาน ดังนั้นการสั่ง docker compose pull ในอีกหลายเดือนถัดมาอาจกลายเป็นการ migration schema โดยไม่ได้วางแผนไว้บนฐานข้อมูลที่คุณไม่ได้สำรองข้อมูลไว้ในเช้าวันนั้น ให้กำหนดเวอร์ชันทั้งสองให้คงที่ในไฟล์ docker-compose.override.yml ซึ่ง 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

เวอร์ชัน 4.3.1 เป็นเวอร์ชันล่าสุดของ 4.3 ณ เดือนสิงหาคม 2026 (เวอร์ชัน 4.4.0 ได้ปล่อยออกมาแล้ว) ให้ตรวจสอบหน้า GitHub releases ของโปรเจกต์ กำหนดเวอร์ชันที่เป็นปัจจุบันในวันที่คุณติดตั้ง แล้วค่อยอัปเดตเวอร์ชันอย่างตั้งใจในภายหลัง สำหรับ image ของระบบจัดเก็บข้อมูลที่ระบุมาในไฟล์หลักนั้นถูกกำหนดเวอร์ชันหลักไว้แล้ว ได้แก่ postgres:17, clickhouse-server:25.12 และ redis:7 ซึ่งควรได้รับการกำหนดเวอร์ชันให้คงที่เช่นเดียวกัน

เริ่มการทำงานของระบบ

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

การบูตครั้งแรกจะรันการ migration ดังนั้นควรให้เวลาระบบสักหนึ่งหรือสองนาทีก่อนที่จะเริ่มใช้งาน docker compose ps ควรแสดงรายการบริการ 6 รายการในสถานะ running หาก worker มีการรีสตาร์ทวนซ้ำ ให้ดูสาเหตุใน log: CLICKHOUSE_MIGRATION_URL ใช้โปรโตคอลดั้งเดิมของ ClickHouse บนพอร์ต 9000 ไม่ใช่พอร์ต HTTP 8123 หากชี้ไปที่ 8123 จะทำให้เกิดข้อผิดพลาดในส่วนนั้น ในขณะที่ 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 ยังทำงานอยู่เท่านั้น เนื่องจากมันข้ามการตรวจสอบฐานข้อมูลเพื่อให้ระบบยังคงให้บริการได้แม้ Postgres จะมีปัญหาชั่วคราว รูปแบบ failIfDatabaseUnavailable=true คือสิ่งที่ควรนำไปใช้กับระบบ monitor ซึ่งจะส่งค่า 503 กลับมาเมื่อฐานข้อมูลไม่สามารถเข้าถึงได้ ส่วน /api/public/ready จะส่งค่า 200 กลับมาเมื่อการ migration เสร็จสิ้นและ container พร้อมรับ traffic ทั้งสองแบบเป็นการตรวจสอบผ่าน HTTP ปกติ ดังนั้นคุณสามารถใช้ หน้าสถานะของ Uptime Kuma เพื่อเฝ้าระวังและแจ้งเตือนคุณหาก stack ล่มก่อนที่ agent ของคุณจะได้รับผลกระทบ

วาง TLS ไว้ด้านหน้าและปิดพอร์ตส่วนเกิน

ไฟล์ compose ที่มาพร้อมกับซอฟต์แวร์จะเผยแพร่ 3000:3000 สำหรับคอนเทนเนอร์เว็บ และ 9090:9000 สำหรับ MinIO โดยทั้งคู่จะผูกกับทุกอินเทอร์เฟซ หากรันบน IP สาธารณะ หมายความว่าใครก็ตามที่สแกนพอร์ต 3000 จะเข้าถึงหน้าลงทะเบียนของคุณได้ และใครที่สแกน 9090 จะสามารถสื่อสารกับ bucket ที่เก็บข้อมูลดิบของคุณได้โดยตรง

กฎของไฟร์วอลล์เพียงอย่างเดียวไม่สามารถปิดพอร์ตเหล่านี้ได้ Docker จะเขียนกฎ DNAT ของตัวเองลงในตาราง nat ซึ่งกฎเหล่านี้จะถูกประเมินก่อนที่กฎ filter ของ ufw จะเห็นแพ็กเก็ต ดังนั้น ufw deny 3000 จึงทำให้พอร์ตที่เผยแพร่นั้นยังคงเปิดอยู่ ปัญหานี้เกิดขึ้นบ่อยจนมีคู่มือเฉพาะทาง: เหตุใดพอร์ตที่เผยแพร่โดย Docker จึงข้าม ufw ให้เปลี่ยนไปผูกกับ loopback ในไฟล์ override ของคุณแทน

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 จากค่านี้ หากปล่อยไว้เป็น http://localhost:3000 ในขณะที่อยู่หลัง HTTPS proxy การทำ sign in round trip จะส่งเบราว์เซอร์ไปยังที่ที่เข้าถึงไม่ได้

จากนั้นให้ชี้ reverse proxy ไปที่ 127.0.0.1:3000 และให้มันเป็นผู้จัดการ certificate โดยทั่วไปมักใช้ Traefik ในโปรเจกต์ Compose เดียวกัน ซึ่ง label สำหรับการทำ routing จะเป็นไปตามที่อธิบายไว้ใน การรันหลายแอปหลัง Traefik reverse proxy ตัวเดียว หาก Langfuse เป็นแอปเดียวบนเซิร์ฟเวอร์ Caddy สามารถทำงานเดียวกันได้โดยใช้เพียงสองบรรทัด ตรวจสอบความถูกต้องด้วย curl -sI https://langfuse.example.com/api/public/ready จากนั้นยืนยันจากเครื่องอื่นว่า curl http://YOUR_IP:3000 ไม่สามารถเชื่อมต่อได้แล้ว (time out)

ข้อควรระวังสำหรับ MinIO คือ Langfuse จะส่งสื่อที่แนบมาไปยังเบราว์เซอร์ของคุณผ่าน presigned URL ที่ชี้ไปยัง S3 endpoint นั้น ดังนั้นหากคุณใช้ traces แบบหลายสื่อ (multi-modal) ที่มีรูปภาพหรือเสียง การตั้งค่า MinIO ให้เป็น loopback-only จะทำให้ไฟล์แนบเหล่านั้นไม่แสดงผล โปรดอ่านหน้าการตั้งค่า blob storage ก่อนที่จะทำ proxy เพราะ endpoint ที่เขียนลงใน presigned URL จะต้องตรงกับสิ่งที่คุณเผยแพร่ออกไป สำหรับ traces ที่เป็นข้อความธรรมดาจะไม่ได้รับผลกระทบนี้

สร้างบัญชีของคุณในการเข้าใช้งานครั้งแรก จากนั้นจำกัดสิทธิ์การเข้าถึงไว้เฉพาะคุณ ตั้งค่า LANGFUSE_ALLOWED_ORGANIZATION_CREATORS ให้เป็นที่อยู่อีเมลของคุณ เพื่อป้องกันไม่ให้คนแปลกหน้าที่เข้าถึงหน้าเว็บสามารถสร้างองค์กรบนเซิร์ฟเวอร์ของคุณได้ หากคุณกำลังใช้งาน Authentik เป็น identity provider ของคุณเอง อยู่แล้ว Langfuse สามารถเชื่อมต่อผ่าน OIDC มาตรฐานได้ ทำให้บัญชีผู้ใช้สามารถจัดการร่วมกับแอปอื่นๆ ของคุณได้ แทนที่จะต้องเก็บรหัสผ่านแยกไว้ในรายการที่เซิร์ฟเวอร์นี้รู้จักเพียงเครื่องเดียว

ส่ง trace แรกของคุณ

สร้างโปรเจกต์ในเว็บอินเทอร์เฟซและคัดลอก public key และ secret key จากการตั้งค่าโปรเจกต์ Python SDK จะอ่านค่าจาก environment variable สามตัว

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 ซึ่งเปิดตัวในเดือนมีนาคม 2026 โค้ดและคู่มือรุ่นเก่าจะใช้ 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()

decorator @observe จะเปิดการสังเกตการณ์ (observation) รอบฟังก์ชัน โดยจะบันทึกอาร์กิวเมนต์และค่าที่ส่งกลับมา และจัดลำดับให้อยู่ภายใต้การสังเกตการณ์ที่กำลังทำงานอยู่ AnthropicInstrumentor คือเครื่องมือ OpenTelemetry สำหรับ Anthropic client ซึ่งจะเปลี่ยนการเรียก messages.create แต่ละครั้งให้เป็น generation ที่ระบุชื่อโมเดล, การใช้โทเค็น และค่าความหน่วง (latency) โดยไม่ต้องแก้ไขโค้ดที่จุดเรียกใช้งาน

การเรียกใช้งานสองคำสั่งจะช่วยตรวจสอบสถานะให้คุณ langfuse.auth_check() จะส่งค่า False หากคีย์ไม่ถูกต้องหรือ base URL ผิดพลาด ซึ่งช่วยให้ตรวจสอบได้รวดเร็วกว่าการสงสัยว่าเหตุใดแดชบอร์ดจึงว่างเปล่า langfuse.flush() จะรอจนกว่า span ที่อยู่ในคิวจะถูกส่งออกไปจนหมด ซึ่งจำเป็นสำหรับกระบวนการที่ทำงานสั้นๆ เนื่องจาก SDK จะทำการ batch ข้อมูลในเบื้องหลัง หากสคริปต์จบการทำงานทันที ข้อมูลที่ยังไม่ได้ส่งใน batch จะสูญหายไปพร้อมกับกระบวนการนั้น

เหตุใด ClickHouse ถึงมีขนาดเพิ่มขึ้นเรื่อยๆ

Traces เป็นข้อมูลที่เติบโตเร็วที่สุดเท่าที่ผู้ใช้งานส่วนใหญ่เคยทำ self-host มา ทุกครั้งที่ agent ทำงานจะเขียนข้อมูลหนึ่งแถวต่อหนึ่งขั้นตอน และมีการจัดเก็บข้อมูล input และ output แบบเต็มรูปแบบ ดังนั้น agent ที่มีการทำงานถี่และมี prompt ที่ยาวจะสร้างข้อมูลจำนวนมหาศาลต่อวันมากกว่าแอปพลิเคชันที่มันกำลังตรวจสอบอยู่ หากปล่อยทิ้งไว้ ClickHouse จะเต็มดิสก์ และเมื่อดิสก์เต็มจะทำให้การรับข้อมูล (ingestion) หยุดทำงานทันทีแทนที่จะเป็นการทำงานที่ช้าลง

การเติบโตนี้เกิดจากสองส่วนแยกกัน ซึ่งต้องใช้วิธีแก้ไขที่แตกต่างกัน

ส่วนแรกคือข้อมูล trace ของคุณเอง วิธีแก้ไขคือการตั้งค่า retention ในหน้าการตั้งค่าโปรเจกต์บนเว็บอินเทอร์เฟซ ให้กำหนดระยะเวลาการเก็บรักษาข้อมูลเป็นจำนวนวัน Langfuse รองรับขั้นต่ำที่ 3 วัน จากนั้นจะมีงานที่ทำงานทุกคืนเพื่อคัดเลือก traces, observations, scores และ media assets ที่เก่าเกินกว่าช่วงเวลาที่กำหนดและลบออกจาก ClickHouse รวมถึง blob storage งานนี้ต้องการสิทธิ์ DeleteObject บน bucket ซึ่ง MinIO root credentials ในไฟล์ compose เริ่มต้นมีสิทธิ์นี้อยู่แล้ว การลบข้อมูลเป็นการลบอย่างถาวร ดังนั้นหากคุณต้องการประวัติข้อมูลระยะยาว ให้กำหนดค่าการส่งออก (export) ไปยัง blob storage ก่อน อย่าเขียนคำสั่ง TTL ลงบนตารางของ Langfuse เองโดยตรง เพราะงาน retention คือสิ่งที่ทำให้ ClickHouse และ bucket ทำงานสอดคล้องกัน การใช้ TTL ด้วยตนเองจะทำให้ข้อมูลถูกลบเพียงฝั่งเดียว

ให้เลือกช่วงเวลาตามการใช้งานจริงของคุณ การตรวจสอบต้นทุนและคุณภาพมักทำกับข้อมูลที่ย้อนหลังไปไม่กี่วัน ไม่ใช่หลายเดือน 30 วันเป็นจุดเริ่มต้นที่เหมาะสมสำหรับทีมขนาดเล็ก และ 14 วันก็เพียงพอหากคุณเปิดดู trace เฉพาะตอนที่ระบบมีปัญหาเท่านั้น

ส่วนที่สองคือตาราง system log ของ ClickHouse เอง ซึ่งมักทำให้ผู้ใช้งานประหลาดใจเพราะดิสก์ยังคงมีขนาดเพิ่มขึ้นแม้จะตั้งค่า retention แล้ว ClickHouse จะเขียน trace_log, text_log, opentelemetry_span_log, metric_log และ asynchronous_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" หากตารางระบบอยู่ในลำดับต้นๆ ของการใช้พื้นที่ ให้ปิดการใช้งานด้วย config overlay เพราะ ClickHouse จะรวมไฟล์ทุกไฟล์ใน /etc/clickhouse-server/config.d/ เข้ากับ config หลักเมื่อเริ่มต้นระบบ

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

ให้ mount ไฟล์นี้และรีสตาร์ท 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" ซึ่งมีรายละเอียดอยู่ในเอกสารการขยายระบบ (scaling docs) ของ Langfuse

มีอีกหนึ่งตารางที่ควรทราบคือ blob_storage_file_log ซึ่งใช้ติดตามไฟล์ event ที่อัปโหลดไปยัง bucket ของคุณ หากคุณตั้งค่านโยบาย lifecycle บน bucket ไว้ด้วย ให้กำหนด TTL บนตารางให้ตรงกันเพื่อไม่ให้ข้อมูลทั้งสองฝั่งคลาดเคลื่อนกัน

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

ให้ตั้งค่าการแจ้งเตือน df -h บนดิสก์ข้อมูลไว้ด้วย Traces ไม่ได้เติบโตอย่างสม่ำเสมอ แต่มันจะเพิ่มขึ้นในวันที่คุณปล่อย agent ตัวใหม่ และสัญญาณแรกของเรื่องนี้ไม่ควรเป็นการที่ระบบหยุดรับข้อมูล

การสำรองข้อมูล Postgres และ ClickHouse

การสำรองข้อมูล Langfuse ประกอบด้วย 3 ส่วน Postgres เก็บข้อมูลผู้ใช้ องค์กร โปรเจกต์ และ API keys ส่วน ClickHouse เก็บข้อมูล traces และ MinIO เก็บข้อมูลเหตุการณ์ดิบ (raw events) หากกู้คืนเฉพาะ Postgres คุณจะเข้าสู่ระบบได้แต่ไม่มีประวัติการใช้งาน หากกู้คืนเฉพาะ ClickHouse คุณจะมีประวัติแต่ไม่มีใครสามารถเข้าสู่ระบบเพื่อดูข้อมูลได้

Postgres เป็นเพียง pg_dump ซึ่งเป็นวิธีที่เอกสารการสำรองข้อมูลของ Langfuse แนะนำ

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

ClickHouse ต้องใช้ความระมัดระวังมากกว่า เนื่องจากไดเรกทอรีข้อมูลที่คัดลอกขณะที่ระบบกำลังทำ merges จะไม่ใช่การสำรองข้อมูลที่สอดคล้องกัน วิธีที่ง่ายที่สุดบนเซิร์ฟเวอร์เครื่องเดียวคือการหยุด container แล้วทำ archive ข้อมูลใน 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

ให้ใช้ชื่อ volume ที่แสดงโดย docker volume ls อย่าใช้ชื่อที่เขียนไว้ในไฟล์ YAML เนื่องจากไฟล์ระบุไว้เป็น langfuse_clickhouse_data และ Docker Compose จะเติมชื่อโปรเจกต์ไว้ข้างหน้า ดังนั้นหากคุณ clone ในไดเรกทอรีที่ชื่อ langfuse จะได้ชื่อเป็น langfuse_langfuse_clickhouse_data หากระบุชื่อผิด docker run จะสร้าง volume ใหม่ที่ว่างเปล่าโดยไม่มีการแจ้งเตือน และไฟล์ archive ของคุณจะไม่มีข้อมูลใดๆ

container ส่วน web จะเขียนเหตุการณ์ที่เข้ามาทั้งหมดลงใน bucket ก่อนที่ worker จะประมวลผล ดังนั้นการหยุด ClickHouse ในระยะเวลาสั้นๆ ส่วนใหญ่จะทำให้ worker กลับมาประมวลผลใหม่ในภายหลัง ควรดำเนินการในช่วงที่มีการใช้งานน้อยและใช้เวลาให้สั้นที่สุด สำหรับ instance ที่มีงานหนาแน่น คำสั่ง BACKUP DATABASE default TO S3(...) ของ ClickHouse เองสามารถเขียนข้อมูลสำรองที่สอดคล้องกันได้โดยไม่ต้องหยุดเซิร์ฟเวอร์ MinIO เป็นส่วนที่สาม ซึ่งการใช้ mc mirror หรือการทำ MinIO replication ไปยัง bucket นอกเซิร์ฟเวอร์จะครอบคลุมส่วนนี้ ไม่ว่าคุณจะใช้วิธีใด ให้ย้ายข้อมูลออกจากเซิร์ฟเวอร์ ซึ่งเป็นสิ่งที่ การสำรองข้อมูลด้วย restic แบบเข้ารหัสบน VPS มีไว้เพื่อการนี้

Redis ไม่จำเป็นต้องสำรองข้อมูล เนื่องจากเก็บข้อมูลคิวและแคช การสูญเสียข้อมูลส่วนนี้จะทำให้เสียเฉพาะเหตุการณ์ที่กำลังประมวลผลอยู่เท่านั้น แต่ไม่กระทบต่อข้อมูลเก่า

ข้อควรระวังเรื่องความสอดคล้องของข้อมูลเป็นเรื่องจริงและควรระบุให้ชัดเจน เนื่องจาก Postgres และ ClickHouse ถูก dump ในเวลาที่ต่างกัน การกู้คืนอาจทำให้เกิดกรณีที่มีแถวข้อมูลโปรเจกต์แต่ไม่มี traces หรือมี traces ที่เป็นของโปรเจกต์ที่ไม่มีอยู่จริง Langfuse สามารถรองรับสถานการณ์นี้ได้ แต่ควรทำการ dump ข้อมูลทั้งสองส่วนให้ใกล้เคียงกันที่สุดในช่วงที่มีการใช้งานต่ำ Event bucket คือตาข่ายรองรับที่แท้จริง เพราะ Langfuse จะบันทึกทุกเหตุการณ์ที่เข้ามาลงในนั้นก่อนเริ่มประมวลผลเสมอ

ควรทดสอบการกู้คืนลงใน stack สำรองอย่างน้อยหนึ่งครั้ง นี่คือวิธีที่คุณจะพบข้อผิดพลาดเรื่องชื่อ volume ในตอนนี้ แทนที่จะไปพบในระหว่างที่ระบบเกิดปัญหาจริง

สิ่งที่ควรตรวจสอบเป็นอันดับแรก

มีสี่สิ่งที่ควรให้ความสำคัญในช่วงสัปดาห์แรก

  • ต้นทุนต่อหนึ่ง trace. Langfuse คำนวณต้นทุนจากชื่อโมเดลและการใช้งาน token ดังนั้นให้เรียงลำดับ trace ตามต้นทุนและอ่าน trace ที่มีราคาสูงที่สุดตั้งแต่ต้นจนจบ คำตอบมักจะเป็น prompt ที่ขยายตัวขึ้น เช่น การวางเอกสารทั้งฉบับลงใน context หรือประวัติการสนทนาที่ไม่มีการตัดทอน เมื่อคุณเห็นปัญหาแล้ว การควบคุมต้นทุนของ AI agent จะกลายเป็นงานด้านวิศวกรรมแทนที่จะเป็นการคาดเดา
  • การแบ่งสัดส่วนการใช้งาน token ระหว่าง input และ output. Input token มีจำนวนมากและราคาถูก ส่วน output token มีจำนวนน้อยและราคาสูง ในขณะที่ cached input มีราคาถูกลงไปอีก การคำนวณแบบเดียวกันนี้ถูกอธิบายไว้ใน วิธีการนับ token ของ Claude Code และสามารถนำไปประยุกต์ใช้กับ agent ใดก็ตามที่คุณเขียนขึ้นเอง
  • เปอร์เซ็นไทล์ของ Latency. ค่ามัธยฐานมักจะปิดบังปัญหาที่แท้จริง ค่า p95 และ p99 คือจุดที่เกิดปัญหา timeout และภายในลูปของ agent การเรียกใช้เครื่องมือที่ล่าช้าที่ระดับ p95 จะถูกทวีคูณตามจำนวนรอบการทำงาน
  • การเรียกใช้เครื่องมือที่ล้มเหลว. กรองข้อมูลการสังเกตการณ์ด้วยระดับ ERROR เครื่องมือที่ล้มเหลว 5% ของเวลาทั้งหมดอาจมองไม่เห็นในอัตราความสำเร็จโดยรวม แต่จะเห็นได้ชัดเจนใน trace ซึ่งคุณจะเห็นโมเดลพยายามทำซ้ำและสิ้นเปลือง token ไปกับการแก้ไขปัญหาดังกล่าว

กำหนดระยะเวลาการเก็บรักษาข้อมูลและเลือก dashboard ที่คุณจะตรวจสอบเป็นประจำทุกสัปดาห์ในวันเดียวกับที่คุณ deploy ระบบ เครื่องมือ observability ที่ไม่มีใครเปิดดู ก็เป็นเพียงฐานข้อมูลที่ทำให้ disk เต็มเท่านั้น

FAQ

Langfuse ที่โฮสต์เองต้องการหน่วยความจำเท่าใด?

ควรวางแผนไว้ที่ 4 CPU cores และหน่วยความจำ 16 GiB ซึ่งเป็นค่าที่คู่มือ Docker Compose ของ Langfuse แนะนำสำหรับ virtual machine หนึ่งเครื่อง พร้อมพื้นที่จัดเก็บข้อมูลประมาณ 100 GiB ค่าขั้นต่ำของส่วนประกอบที่เผยแพร่คือ 8 GiB สำหรับ ClickHouse และ 4 GiB สำหรับคอนเทนเนอร์ web และ worker แต่ละตัว รวมถึง Postgres, Redis และ MinIO ที่ยังคงต้องการหน่วยความจำเพิ่มเติมจากส่วนนี้ หน่วยความจำ 8 GiB เพียงพอสำหรับการรัน instance ของนักพัฒนาหนึ่งคน แต่ 2 GiB ไม่เพียงพอ เนื่องจาก ClickHouse จะถูก kernel สั่งยุติการทำงานระหว่างการทำ background merges และ dmesg จะแสดง Out of memory: Killed process

ทำไมดิสก์ของ ClickHouse ถึงเต็มอยู่เรื่อยๆ หลังจากตั้งค่าการเก็บรักษาข้อมูล (data retention) แล้ว?

การตั้งค่า retention ครอบคลุมเฉพาะข้อมูลของ Langfuse เท่านั้น ClickHouse จะเขียนตารางวินิจฉัยแยกต่างหาก ได้แก่ trace_log, text_log, opentelemetry_span_log, metric_log และ asynchronous_metric_log ซึ่งตารางเหล่านี้ไม่มีการตั้งค่า TTL มาให้ ให้รันคิวรี system.parts โดยจัดกลุ่มตามตารางเพื่อดูว่าตารางใดมีขนาดใหญ่ที่สุด จากนั้นให้ปิดการใช้งานตารางที่ไม่ได้ใช้ด้วยรายการ remove="1" ในไฟล์ภายใต้ /etc/clickhouse-server/config.d/ แล้วรีสตาร์ท ClickHouse และลบตารางที่มีอยู่ทิ้งเพื่อเรียกคืนพื้นที่ที่ถูกใช้งานไปแล้ว

ระยะเวลาการเก็บรักษาข้อมูลขั้นต่ำใน Langfuse คือเท่าใด?

สามวัน การตั้งค่า retention จะทำแยกตามโปรเจกต์ในการตั้งค่าโปรเจกต์ หรือผ่านทาง projects API โดยจะมีงานที่ทำงานทุกคืนเพื่อลบ traces, observations, scores และสื่อต่างๆ ที่เก่ากว่าช่วงเวลาที่กำหนดออกจากทั้ง ClickHouse และ blob storage การลบข้อมูลไม่สามารถย้อนกลับได้ ดังนั้นควรตั้งค่าการส่งออกข้อมูล (export) ไปยัง blob storage ไว้ก่อนหากคุณต้องการเก็บประวัติข้อมูลที่เกินกว่าช่วงเวลาดังกล่าว

ฉันจำเป็นต้องสำรองข้อมูลทั้ง Postgres และ ClickHouse หรือไม่?

จำเป็น เพราะทั้งสองส่วนเก็บข้อมูลที่แตกต่างกัน Postgres เก็บข้อมูลผู้ใช้, องค์กร, โปรเจกต์ และ API keys ส่วน ClickHouse เก็บข้อมูล trace เอง การกู้คืนเฉพาะ Postgres จะทำให้คุณได้ instance ที่ล็อกอินเข้าใช้งานได้แต่ไม่มีข้อมูลใดๆ อยู่เลย นอกจากนี้ควรสำรองข้อมูล MinIO bucket ด้วย เนื่องจากเป็นที่เก็บ raw events ที่ Langfuse บันทึกไว้ทันทีที่ได้รับ ซึ่งถือเป็นแหล่งข้อมูลที่ใกล้เคียงกับแหล่งข้อมูลหลัก (source of truth) ที่สุดใน stack นี้

ฉันสามารถชี้การตั้งค่า OpenTelemetry ที่มีอยู่ไปยัง Langfuse ที่โฮสต์เองได้หรือไม่?

ได้ Langfuse v4 และ SDK เวอร์ชัน v4 ถูกสร้างขึ้นบน OpenTelemetry และเครื่องมือวัด (instrumentations) ของ Anthropic และ OpenAI OTel สามารถส่งข้อมูลไปยัง Langfuse ได้โดยตรง สำหรับ Python ให้รัน pip install langfuse opentelemetry-instrumentation-anthropic, เรียกใช้ AnthropicInstrumentor().instrument() หนึ่งครั้งตอนเริ่มต้นระบบ และตั้งค่า LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY และ LANGFUSE_BASE_URL ไปยังโฮสต์ของคุณเอง ตรวจสอบความถูกต้องด้วย langfuse.auth_check() ก่อนที่จะเริ่มหาสาเหตุหาก dashboard ไม่แสดงข้อมูล