SSD Nodes Learn Hosting plans →
가이드 Matt Connor작성자 Matt Connor

Langfuse 셀프 호스팅 가이드: VPS 구축 및 운영 핵심

Langfuse를 자체 VPS에 구축할 때 필요한 리소스 요구사항과 ClickHouse 데이터 보존 설정, TLS 적용 및 안정적인 백업 방법을 다룹니다. v4 버전의 컨테이너 구성과 디스크 용량 문제를 방지하는 실무적인 운영 노하우를 확인하십시오.

AI 에이전트를 추적해야 하는 이유

Langfuse를 직접 호스팅하면 에이전트가 실행 중에 실제로 어떤 작업을 수행했는지 확인할 수 있습니다. Langfuse는 오픈 소스 LLM(거대 언어 모델) 관측성 도구입니다. 모든 프롬프트, 모델 응답, 도구 호출 및 토큰을 기록한 뒤, 이를 열람 가능한 하나의 트레이스로 그룹화합니다. 자체 VPS에서 실행하면 이러한 프롬프트가 사용자가 제어하는 서버 외부로 유출되지 않습니다.

추적을 수행해야 하는 이유는 명확합니다. 볼 수 없는 비용 문제나 품질 문제는 해결할 수 없기 때문입니다. 공급자의 청구서에는 화요일 비용이 월요일보다 4배 더 많이 발생했다는 사실만 적혀 있습니다. 반면 트레이스를 확인하면 어떤 에이전트 실행이 원인인지, 어떤 프롬프트가 40,000 토큰까지 증가했는지, 어떤 재시도 루프가 9번 반복된 후 종료되었는지 알 수 있습니다. 청구서는 결과 수치만 보여주지만, 트레이스는 그 수치를 만들어낸 코드를 보여줍니다.

이 가이드에서는 세 가지 용어를 사용합니다. 트레이스(trace)는 에이전트의 전체 실행 과정을 의미합니다. 관측(observation)은 실행 내부의 한 단계를 의미하며, 일반 코드의 경우 스팬(span), 모델 호출의 경우 생성(generation)으로 구분합니다. 점수(score)는 사람의 검토나 자동 평가기를 통해 트레이스에 부여된 수치입니다. Langfuse는 분산 추적을 위한 벤더 중립 표준인 OpenTelemetry(OTel)를 지원하므로, 이미 구축된 계측 환경을 그대로 연동할 수 있습니다.

Langfuse 셀프 호스팅의 실제 구성

Langfuse v4는 단일 컨테이너가 아닙니다. 두 개의 애플리케이션 컨테이너와 네 개의 저장소 서비스로 구성되며, 단일 VPS 환경에서는 이 여섯 가지가 모두 서버에서 실행됩니다.

  • langfuse-web은 웹 인터페이스와 수집 API를 제공합니다.
  • langfuse-worker은 백그라운드에서 큐를 처리합니다. 수집된 배치를 파싱하고, 비용을 계산하며, 야간 보존 작업을 수행합니다.
  • Postgres는 사용자, 조직, 프로젝트, API 키, 프롬프트와 같은 트랜잭션 데이터를 저장합니다.
  • ClickHouse는 추적 데이터 자체(관측값 및 점수)를 저장합니다. 분석 쿼리에 최적화된 컬럼형 저장소이므로 수억 개의 행이 있는 대시보드도 빠르게 응답합니다.
  • Redis는 웹과 워커 사이에서 큐 및 캐시 역할을 합니다.
  • MinIO는 서버 내에서 S3 호환 객체 스토리지를 제공합니다. 모든 원본 수신 이벤트와 첨부된 미디어를 저장합니다.

Langfuse는 각 구성 요소에 필요한 최소 리소스를 공개하고 있습니다. 다음 표는 작업을 수행하는 세 가지 핵심 요소를 보여줍니다.

차트Langfuse published minimum resources per component
이 차트의 데이터
[
  {
    "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의 메모리를 요구합니다. 웹 컨테이너와 워커는 각각 4 GiB를 요구합니다. 이는 표에 명시된 3개 구성 요소의 최소 사양입니다. 같은 페이지에서 Postgres는 4 GiB, Redis 또는 Valkey는 1.5 GiB, MinIO는 4 GiB를 요구하므로, 여섯 가지 구성 요소의 최소 사양 합계는 25.5 GiB에 달합니다. 프로젝트의 Docker Compose 가이드에서는 단일 머신 기준으로 최소 4코어, 16 GiB 메모리, 약 100 GiB의 스토리지를 권장합니다. 이는 각 구성 요소의 최소 사양 합계보다 적으므로, 16 GiB를 여유 공간이 아닌 공유 Compose 환경의 절대적인 최소치로 간주해야 합니다.

2 GiB 플랜에서 시도하지 마십시오. ClickHouse는 시작 후 잠시 동안 쓰기를 허용하다가 백그라운드 병합 과정에서 종료됩니다. 병합 작업은 테이블의 큰 부분을 메모리에 로드하기 때문입니다. 이때 docker compose ps은 clickhouse 컨테이너를 restarting 상태로 보고하며, dmesg에는 Out of memory: Killed process 1234 (clickhouse-serv)과 같은 메시지가 기록되고, 모든 Langfuse 대시보드는 500 오류를 반환합니다. 부하가 적을 때는 ClickHouse가 쿼리를 거부하고 DB::Exception: Memory limit (total) exceeded를 로그에 남깁니다. 계획 수립 시 16 GiB를 기준으로 삼으십시오. 동일한 VPS에서 다른 서비스를 운영할 계획이라면 해당 리소스를 별도로 예산에 포함해야 합니다. 자체 호스팅 AFFiNE 워크스페이스와 같이 비교적 가벼운 스택조차도 수 기가바이트의 메모리를 점유하며, ClickHouse는 할당된 메모리를 반환하지 않기 때문입니다.

Docker Compose를 사용하여 Langfuse 배포하기

저장소를 복제합니다. 스택, 연결 설정 및 기본 환경 변수는 모두 docker-compose.yml에 포함되어 있습니다.

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

변경해야 할 모든 값은 해당 파일 내에 # CHANGEME로 표시되어 있습니다. 먼저 세 가지 애플리케이션 보안 키를 생성합니다.

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

ENCRYPTION_KEY는 64자 16진수로 작성된 256비트 값이어야 하며, 이는 openssl rand -hex 32 명령어가 출력하는 값과 정확히 일치합니다. 이 값은 인스턴스에 저장된 LLM 제공업체 키를 포함하여 저장된 민감한 데이터를 암호화합니다. 데이터가 생성된 후 이 값을 변경하면 기존 데이터를 복호화할 수 없으므로, 첫 부팅 시점부터 영구적인 값으로 취급해야 합니다. SALT은 Langfuse API 키를 해싱하는 데 사용되므로, 이 값을 변경하면 에이전트가 이미 사용 중인 모든 키가 무효화됩니다.

그런 다음 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는 SignatureDoesNotMatch 오류와 함께 클라이언트 연결을 거부하며, 웹 인터페이스는 정상으로 보여도 워커 로그에는 해당 오류가 기록됩니다. 추적되는 compose 파일 대신 env 파일에 이러한 값을 보관하는 방식은 Docker Compose env 파일 및 보안 키에서 다룹니다.

시작 전 이미지 태그 고정하기

제공된 파일은 docker.langfuse.com/langfuse/langfuse:4 및 docker.langfuse.com/langfuse/langfuse-worker:4을 사용합니다. 이 태그들은 수시로 변경됩니다. Langfuse는 시작 시 Postgres 및 ClickHouse 마이그레이션을 자동으로 실행하므로, 몇 달 뒤 일상적인 docker compose pull를 수행하면 당일 백업하지 않은 데이터베이스에서 계획되지 않은 스키마 마이그레이션이 발생할 수 있습니다. 두 이미지 모두 docker-compose.override.yml에서 특정 릴리스 버전으로 고정하십시오. Compose는 이를 기존 파일 위에 병합하므로 나중에 git pull을 수행해도 편집 내용이 덮어씌워지지 않습니다. 동일한 이미지가 Docker Hub에 게시되어 있으며, 아래의 오버라이드 설정은 해당 저장소에서 이미지를 가져옵니다.

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

버전 4.3.1은 2026년 8월 기준 최신 4.3 릴리스였습니다. 이후 릴리스가 빠르게 이어져 2026년 10월 11일 기준 GitHub 최신 버전은 v4.56.0이었습니다. 위 오버라이드 설정은 2026년 10월 31일 기준 GitHub 최신 버전인 v4.72.0을 사용합니다. 프로젝트의 GitHub 릴리스 페이지를 확인하여 배포 당일의 최신 버전을 고정하고, 이후 의도적으로 버전을 업데이트하십시오. 제공된 파일의 일부 스토리지 이미지도 태그가 변경되므로 동일한 처리가 필요합니다. postgres:17(POSTGRES_VERSION 변수의 기본값) 및 redis:7는 메이저 버전을 따릅니다. 현재 제공된 파일은 docker.io/clickhouse/clickhouse-server에 정확한 버전 태그를 사용하여 ClickHouse 버전을 고정하고 있습니다. 이 규칙은 Langfuse에만 국한되지 않습니다. 자체 호스팅 openGym 운동 추적기는 이 스택의 일부만 사용함에도 불구하고 명시적인 git 태그를 사용합니다. 시작 시 자체 데이터베이스를 마이그레이션하는 모든 소프트웨어는 일상적인 pull 작업을 스키마 변경으로 바꿀 수 있기 때문입니다.

서비스를 시작합니다.

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

첫 부팅 시 마이그레이션이 실행되므로 서비스가 응답하기까지 2~3분 정도 기다려야 합니다. 웹 컨테이너는 준비가 완료되면 Ready을 로그에 출력합니다. docker compose ps 명령을 실행하면 6개의 서비스가 running 상태로 나열되어야 합니다. 컨테이너가 반복적으로 재시작된다면 로그에서 원인을 확인할 수 있습니다. ClickHouse 마이그레이션은 웹 컨테이너가 시작될 때 적용되므로, 잘못된 CLICKHOUSE_MIGRATION_URL 설정은 추적 중인 워커 로그가 아닌 docker compose logs langfuse-web에서 확인할 수 있습니다. 해당 변수는 HTTP 포트 8123이 아닌 ClickHouse 네이티브 프로토콜 포트 9000을 사용하며, 8123으로 설정하면 웹 컨테이너의 시작 마이그레이션이 실패합니다. 워커는 더 느린 백그라운드 마이그레이션을 실행하므로 해당 로그에서 관련 보고를 확인할 수 있습니다.

서버 내부에서 상태를 확인합니다.

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 프로세스가 살아있다는 것만 증명합니다. failIfDatabaseUnavailable=true 형태의 엔드포인트를 모니터링 대상으로 지정해야 하며, 데이터베이스에 연결할 수 없을 때 503을 반환합니다. /api/public/ready은 웹 컨테이너가 트래픽을 받을 준비가 되었을 때 200을 반환하고, 종료 명령을 받았을 때는 500을 반환합니다. 둘 다 일반적인 HTTP 체크이므로 Uptime Kuma 상태 페이지를 통해 이를 감시하면 에이전트보다 먼저 스택이 다운되었음을 알 수 있습니다.

TLS를 앞단에 배치하고 추가 포트 닫기

제공된 compose 파일은 웹 컨테이너를 위해 3000:3000을, MinIO를 위해 9090:9000를 게시합니다. 두 포트 모두 모든 인터페이스에 바인딩됩니다. 공인 IP 환경에서 이는 포트 3000을 스캔하는 누구나 귀하의 가입 페이지에 접근할 수 있고, 9090을 스캔하는 누구나 원시 프롬프트가 저장된 버킷과 통신할 수 있음을 의미합니다.

방화벽 규칙만으로는 이 포트들을 닫을 수 없습니다. Docker는 자체 DNAT 규칙을 nat 테이블에 작성하며, 이 규칙은 ufw의 필터 규칙이 패킷을 확인하기 전에 평가되므로 ufw deny 3000은 게시된 포트를 열린 상태로 둡니다. 이 문제는 흔히 발생하여 별도의 가이드가 존재합니다: Docker 게시 포트가 ufw를 우회하는 이유. 대신 override 파일에서 루프백(loopback)에 바인딩하십시오. Docker Compose 2.24.4 이상 버전에서 !override를 사용하여 게시된 포트를 교체하십시오. 단순 목록은 기존 바인딩과 병합됩니다.

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

NEXTAUTH_URL는 스킴을 포함한 정확한 공인 주소여야 합니다. 로그인 흐름이 해당 값을 기반으로 콜백 URL을 생성하기 때문입니다. HTTPS 프록시 뒤에서 이를 http://localhost:3000으로 두면, 로그인 왕복 과정에서 브라우저가 도달할 수 없는 곳으로 전송됩니다.

이제 리버스 프록시를 127.0.0.1:3000로 지정하고 인증서를 관리하게 하십시오. 동일한 Compose 프로젝트 내에서 Traefik을 사용하는 경우, 하나의 Traefik 리버스 프록시 뒤에서 여러 앱 실행하기에서 다루는 레이블을 사용하여 Compose 네트워크를 통해 langfuse-web:3000로 라우팅하십시오. 서버에 Langfuse만 운영 중이라면 Caddy로도 두 줄 만에 동일한 작업을 수행할 수 있습니다. curl -sI https://langfuse.example.com/api/public/ready로 확인한 다음, 다른 장비에서 curl http://YOUR_IP:3000에 연결할 수 없음을 확인하십시오.

MinIO에 관한 주의 사항이 하나 있습니다. Langfuse는 미디어를 업로드하는 SDK와 이를 불러오는 브라우저 모두에 해당 S3 엔드포인트를 가리키는 사전 서명된(presigned) URL을 제공합니다. 따라서 이미지나 오디오가 포함된 멀티모달 트레이스를 사용하는 경우, 루프백 전용 MinIO를 사용하면 다른 장비에서 해당 첨부 파일을 업로드하거나 브라우저에서 불러올 수 없게 됩니다. 프록시를 설정하기 전에 블록 스토리지(blob storage) 구성 페이지를 읽어보십시오. 사전 서명된 URL에 기록되는 엔드포인트인 LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT이 귀하가 게시하는 주소와 일치해야 하기 때문입니다. 일반 텍스트 트레이스는 영향을 받지 않습니다.

첫 방문 시 계정을 생성한 후, 인스턴스를 비공개로 유지하십시오. 계정 생성이 완료되면 AUTH_DISABLE_SIGNUP=true를 설정하여 페이지에 접근한 낯선 사람이 등록하지 못하도록 하십시오. Langfuse에는 조직 생성 권한을 제한하는 LANGFUSE_ALLOWED_ORGANIZATION_CREATORS도 있지만, 이는 Enterprise Edition 기능이며 라이선스 키가 있어야만 적용됩니다. 이미 자체 ID 공급자로 Authentik을 운영 중이라면, Langfuse는 표준 OIDC 연결을 지원하므로 계정 정보가 이 서버에만 존재하는 비밀번호 목록에 머무르지 않고 다른 앱들과 통합되어 관리됩니다.

첫 번째 트레이스 전송

웹 인터페이스에서 프로젝트를 생성하고 프로젝트 설정에서 공개 키와 비밀 키를 복사합니다. Python SDK는 세 가지 환경 변수를 읽습니다.

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

LANGFUSE_BASE_URL은 2026년 3월에 릴리스된 SDK v4의 변수명입니다. 이전 코드와 가이드는 LANGFUSE_HOST을 사용합니다. 트레이스가 서버가 아닌 Langfuse Cloud로 전송된다면, 기본값이 호스팅된 인스턴스를 가리키기 때문에 베이스 URL이 설정되지 않은 것이 원인입니다.

현재 Python SDK v5는 Python 3.11 이상과 self-hosted Langfuse v4.59.0 이상을 요구하며, 위에서 고정한 서버 버전은 이를 충족합니다.

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 데코레이터는 함수를 감싸는 관찰(observation)을 시작하여 인자와 반환값을 캡처하고, 이미 활성화된 관찰 아래에 중첩합니다. AnthropicInstrumentor은 Anthropic 클라이언트를 위한 OpenTelemetry 계측 도구이며, 호출 지점의 변경 없이 각 messages.create 호출을 모델명, 토큰 사용량, 지연 시간을 포함하는 생성(generation)으로 변환합니다.

두 가지 호출을 통해 확인을 수행할 수 있습니다. langfuse.auth_check()는 잘못된 키나 베이스 URL이 입력된 경우 False를 반환하며, 이는 대시보드가 비어 있는 이유를 고민하는 것보다 빠릅니다. langfuse.flush()은 큐에 쌓인 스팬이 전송될 때까지 차단하며, SDK가 백그라운드에서 배치 처리를 수행하므로 즉시 종료되는 프로세스에서는 이 호출이 필요합니다. 프로세스가 즉시 종료되면 전송되지 않은 배치 데이터도 함께 사라지기 때문입니다. 한 명의 스크립트가 아닌 팀 전체가 트레이스를 전송하는 경우, 각자의 셸에 설정하는 대신 모든 에이전트가 통과하는 게이트웨이에 해당 변수 세 개를 한 번만 설정하십시오. 이것이 self-hosted OneCLI 하네스를 사용하여 키를 한곳에 보관하면서 모든 동료의 실행을 계측하는 방법입니다.

ClickHouse의 용량이 계속 증가하는 이유는 무엇입니까?

트레이스(trace) 데이터는 개인이 직접 호스팅하는 데이터 중 가장 빠르게 증가하는 데이터입니다. 에이전트가 실행될 때마다 단계별로 한 행씩 기록되며, 입력과 출력 전체가 저장됩니다. 따라서 긴 프롬프트를 사용하는 에이전트는 모니터링 대상 애플리케이션보다 훨씬 많은 바이트를 매일 생성합니다. 방치할 경우 ClickHouse는 디스크를 가득 채우게 되며, 디스크가 꽉 차면 데이터 수집 속도가 느려지는 것이 아니라 아예 중단됩니다.

여기에는 두 가지 별개의 증가 요인이 있으며, 각각 다른 해결책이 필요합니다.

첫 번째는 사용자의 트레이스 데이터이며, 해결책은 보존(retention) 설정입니다. 자체 호스팅 인스턴스에서 데이터 보존은 Enterprise Edition 기능이므로 라이선스 키가 필요합니다. 라이선스가 없으면 Langfuse는 트레이스, 관측값(observations), 점수(scores), 미디어를 무기한 보관합니다. 따라서 디스크 용량을 충분히 확보하고 이 섹션 마지막에 설명하는 디스크 경고 기능을 활용해야 합니다. 라이선스가 있다면 프로젝트 소유자와 관리자는 프로젝트 설정에서 데이터 보존 기간을 일 단위로 지정할 수 있습니다. Langfuse는 최소 3일 이상의 보존 기간을 지원합니다. 매일 밤 실행되는 작업이 지정된 기간보다 오래된 트레이스, 관측값, 점수, 미디어 자산을 선택하여 ClickHouse와 블롭(blob) 저장소에서 삭제합니다(단, 저장된 데이터셋 항목과 관련된 미디어는 제외). 이 작업에는 버킷에 대한 DeleteObject 권한이 필요하며, 기본 compose 파일의 MinIO 루트 자격 증명에는 이미 이 권한이 포함되어 있습니다. 삭제는 영구적이므로 장기 기록이 필요하다면 먼저 블롭 저장소 내보내기를 구성하십시오. Langfuse의 테이블에 직접 TTL 구문을 작성하지 마십시오. 보존 작업이 ClickHouse와 버킷의 동기화를 유지하며, 수동으로 TTL을 설정하면 한쪽만 삭제될 수 있습니다.

실제 사용량에 맞춰 기간을 선택하십시오. 비용 및 품질 검토는 수개월 전 데이터가 아닌 며칠 전 데이터를 대상으로 수행합니다. 소규모 팀이라면 30일이 적절한 시작점이며, 문제가 발생했을 때만 트레이스를 확인한다면 14일로도 충분합니다.

두 번째는 ClickHouse 자체 시스템 로그 테이블입니다. 보존 설정을 마친 후에도 디스크 용량이 계속 증가하여 많은 사용자가 당황하곤 합니다. ClickHouse는 진단을 위해 trace_log, text_log, opentelemetry_span_log, metric_log, asynchronous_metric_log, latency_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" 명령으로 실행하십시오. 시스템 테이블이 상위권을 차지하고 있다면 설정 오버레이를 사용하여 해당 기능을 끄십시오. 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"/>
    <latency_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 명령을 사용하여 공간을 명시적으로 회수하십시오. 제거한 각 테이블에 대해서도 동일하게 수행합니다. 진단 데이터를 유지하고 싶다면 remove="1" 대신 각 테이블에 공격적인 TTL을 설정하는 대안이 있으며, 이는 Langfuse 확장성 문서에 자세히 설명되어 있습니다. 해당 TTL 지시문은 시스템 테이블이 처음 생성될 때만 적용되므로, 기존 설치 환경에서는 동일한 문서에 안내된 대로 테이블을 수정해야 합니다.

한 가지 더 알아두어야 할 테이블이 있습니다. blob_storage_file_log는 버킷에 업로드된 이벤트 파일을 추적합니다. 버킷에 수명 주기 정책을 설정했다면, 두 데이터가 어긋나지 않도록 해당 테이블에도 일치하는 TTL을 설정하십시오.

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

데이터 디스크에 일반적인 df -h 경고를 설정하십시오. 트레이스 데이터는 일정하게 증가하지 않습니다. 새로운 에이전트를 배포하는 날 급격히 증가하며, 그 첫 징후가 데이터 수집 실패로 나타나서는 안 됩니다.

Postgres 및 ClickHouse 백업

Langfuse 백업은 세 부분으로 구성됩니다. Postgres는 사용자, 조직, 프로젝트 및 API 키를 저장합니다. ClickHouse는 추적(trace) 데이터를 저장합니다. MinIO는 원시 이벤트 데이터를 저장합니다. Postgres만 복원하면 로그인은 가능하지만 기록이 보이지 않습니다. ClickHouse만 복원하면 기록은 있지만 아무도 로그인할 수 없습니다. 이러한 구조는 Langfuse만의 특징이 아니며, 자체 호스팅 Chatwoot 고객 지원 데스크도 동일한 형태를 띱니다. 업로드 디렉터리 없이 Postgres 덤프만 수행하면 대화 내용은 복원되지만 첨부 파일은 모두 사라집니다.

Postgres는 일반적인 pg_dump을 사용하며, 이는 Langfuse 백업 문서에서 Docker 설치 시 권장하는 방식입니다.

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

ClickHouse는 더 주의가 필요합니다. 병합 작업이 진행 중일 때 데이터 디렉터리를 그대로 복사하면 일관된 백업이 되지 않기 때문입니다. 단일 서버 환경에서 가장 간단한 방법은 컨테이너를 중지하고 볼륨을 아카이브하는 것입니다.

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

YAML 파일에 적힌 이름이 아니라 docker volume ls 명령어가 출력하는 볼륨 이름을 사용해야 합니다. 파일에는 langfuse_clickhouse_data라고 명시되어 있지만, Compose는 프로젝트 이름을 접두사로 붙입니다. 따라서 langfuse이라는 디렉터리에서 복제하면 langfuse_langfuse_clickhouse_data이 생성됩니다. 이름을 잘못 입력하면 docker run는 오류 메시지 없이 새로운 빈 볼륨을 생성하며, 결과적으로 아카이브에는 아무것도 담기지 않게 됩니다.

Langfuse는 모든 수집 요청 시 원시 이벤트 데이터를 버킷에 업로드하고, 실패한 수집 작업을 재시도할 때 이를 다시 읽어 들입니다. 따라서 ClickHouse를 잠시 중지하더라도 워커가 이후에 재시도하게 됩니다. 사용량이 적은 시간에 작업을 수행하고 중지 시간을 짧게 유지하십시오. 인스턴스 사용량이 많다면 ClickHouse의 BACKUP DATABASE default TO S3(...) 문을 사용하여 서버를 중지하지 않고도 일관된 백업을 생성할 수 있습니다. MinIO는 세 번째 구성 요소이며, mc mirror 또는 외부 버킷으로의 MinIO 복제를 통해 백업할 수 있습니다. 어떤 방식을 사용하든 백업 데이터는 서버 외부로 옮겨야 하며, 이를 위해 VPS에서의 암호화된 restic 백업을 활용할 수 있습니다.

Redis는 큐와 캐시를 담당합니다. Redis 데이터가 유실되면 원시 이벤트 파일이 버킷에 남아 있더라도 큐에 대기 중이던 작업은 모두 폐기됩니다.

일관성 문제는 실재하며 명확히 인지해야 합니다. Postgres와 ClickHouse 덤프는 서로 다른 시점에 수행되므로, 복원 후 프로젝트 행은 존재하지만 추적 데이터가 없거나, 존재하지 않는 프로젝트에 속한 추적 데이터가 남을 수 있습니다. Langfuse는 이러한 불일치를 허용하지만, 가급적 두 덤프를 시간 차이를 최소화하여 트래픽이 적은 시간에 수행하십시오. 이벤트 버킷은 Langfuse가 모든 수집 요청 시 원시 데이터를 업로드하므로 실질적인 안전망 역할을 합니다.

최소 한 번은 테스트 환경(scratch stack)에 복원을 시도하십시오. 그래야 장애 상황이 발생했을 때가 아니라 지금 미리 볼륨 이름 오류와 같은 문제를 발견할 수 있습니다.

가장 먼저 확인해야 할 사항

첫 주에는 다음 네 가지를 우선적으로 확인해야 합니다.

  • 트레이스당 비용. Langfuse는 모델명과 토큰 사용량을 기반으로 비용을 계산합니다. 따라서 비용순으로 트레이스를 정렬하고 가장 비용이 많이 발생한 트레이스를 처음부터 끝까지 검토하십시오. 원인은 보통 비대해진 프롬프트입니다. 전체 문서를 컨텍스트에 붙여넣었거나, 아무도 정리하지 않은 대화 기록이 쌓여 있는 경우가 많습니다. 이를 확인하면 AI 에이전트 비용 제어는 추측이 아닌 엔지니어링 작업이 됩니다.
  • 입력 및 출력별 토큰 사용량 분할. 입력 토큰은 양이 많고 저렴하며, 출력 토큰은 양이 적고 비쌉니다. 캐시된 입력은 더 저렴합니다. 동일한 계산 방식이 Claude Code 토큰 사용량 산정 방식에 설명되어 있으며, 이는 직접 작성하는 모든 에이전트에도 적용됩니다.
  • 지연 시간 백분위수. 중앙값은 문제를 숨깁니다. p95와 p99에서 타임아웃이 발생하며, 에이전트 루프 내부에서는 p95의 느린 도구 호출이 반복 횟수만큼 곱해져 성능을 저하시킵니다.
  • 실패한 도구 호출. 레벨 ERROR로 관측값을 필터링하십시오. 5%의 확률로 실패하는 도구는 전체 성공률에서는 보이지 않지만, 트레이스에서는 명확하게 드러납니다. 트레이스를 통해 모델이 재시도하고 이를 우회하기 위해 토큰을 낭비하는 과정을 확인할 수 있습니다.

보존 기간을 설정하고 매주 배포하는 날에 확인할 대시보드를 선택하십시오. 아무도 열어보지 않는 관측 도구는 디스크만 채우는 데이터베이스일 뿐입니다.

FAQ

자체 호스팅 Langfuse는 어느 정도의 메모리가 필요한가요?

CPU 4코어와 16 GiB 메모리를 권장합니다. 이는 단일 가상 머신을 위한 Langfuse Docker Compose 가이드의 권장 사양이며, 약 100 GiB의 저장 공간이 추가로 필요합니다. 공개된 구성 요소별 최소 사양은 ClickHouse에 8 GiB, 웹 및 워커 컨테이너에 각각 4 GiB입니다. 동일한 문서에서 Postgres 4 GiB, Redis 1.5 GiB, MinIO 4 GiB를 명시하고 있으므로, 6개 구성 요소를 모두 합치면 25.5 GiB가 되어 Compose 권장 사양인 16 GiB를 초과하게 됩니다.

데이터 보존 기간을 설정했는데도 ClickHouse 디스크가 계속 꽉 차는 이유는 무엇인가요?

보존 설정은 Langfuse의 자체 데이터에만 적용됩니다. ClickHouse는 별도로 진단 테이블인 trace_log, text_log, opentelemetry_span_log, metric_log, asynchronous_metric_log 및 latency_log에 기록을 남기며, 이 테이블들에는 기본적으로 TTL이 설정되어 있지 않습니다. system.parts 쿼리를 테이블별로 그룹화하여 가장 큰 테이블을 확인한 다음, /etc/clickhouse-server/config.d/ 아래의 파일에 remove="1" 항목을 추가하여 사용하지 않는 테이블을 비활성화하십시오. 이후 ClickHouse를 재시작하고 기존 테이블을 삭제하여 점유된 공간을 확보하십시오.

Langfuse의 최소 데이터 보존 기간은 얼마인가요?

3일입니다. 보존 기간은 프로젝트 설정 또는 Organization Management API를 통해 프로젝트별로 설정할 수 있습니다. 매일 밤 실행되는 작업이 설정된 기간보다 오래된 트레이스, 관측값, 점수 및 미디어 자산을 ClickHouse와 blob 저장소에서 삭제합니다. 단, 저장된 데이터셋 항목과 관련된 미디어는 예외입니다. 자체 호스팅 인스턴스에서 이 기능은 라이선스 키가 필요한 Enterprise Edition 기능입니다. 라이선스가 없으면 데이터는 무기한 보관됩니다. 삭제된 데이터는 복구할 수 없으므로, 해당 기간 이후의 기록이 필요하다면 먼저 blob 저장소 내보내기를 구성하십시오.

Postgres와 ClickHouse를 모두 백업해야 하나요?

네, 두 데이터베이스가 서로 다른 정보를 저장하기 때문입니다. Postgres는 사용자, 조직, 프로젝트 및 API 키를 관리하며, ClickHouse는 트레이스 데이터 자체를 저장합니다. Postgres만 복원하면 로그인은 가능하지만 데이터가 없는 빈 인스턴스가 됩니다. MinIO 버킷도 함께 백업하십시오. MinIO에는 Langfuse가 수신 시점에 영구 저장하는 원시 이벤트가 포함되어 있으며, 이는 전체 스택에서 원천 데이터에 가장 가까운 정보입니다.

기존 OpenTelemetry 설정을 자체 호스팅 Langfuse로 연결할 수 있나요?

네, 가능합니다. Langfuse v4와 현재 SDK는 OpenTelemetry를 기반으로 구축되었으며, Anthropic 및 OpenAI OTel 계측 도구는 이를 직접 내보낼 수 있습니다. Python 환경에서는 pip install langfuse opentelemetry-instrumentation-anthropic를 실행하고, 시작 시 AnthropicInstrumentor().instrument()를 한 번 호출한 뒤, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY 및 LANGFUSE_BASE_URL을 본인의 호스트로 설정하십시오. 대시보드에 데이터가 보이지 않는다면 먼저 langfuse.auth_check()를 통해 연결을 확인하십시오.

이 글이 도움이 되었나요?