Split View: Langfuse 자체 호스팅 — 배포 경로, 시크릿, 그리고 첫 기동에서 걸리는 것들
Langfuse 자체 호스팅 — 배포 경로, 시크릿, 그리고 첫 기동에서 걸리는 것들
- 들어가며 — 무엇을 띄우는지 알고 시작하기
- 가장 빠른 경로 — docker compose
- 반드시 직접 만들어 넣는 값
- 저장소별 연결 설정
- 오브젝트 스토리지 설정
- 쿠버네티스 경로 — 헬름 차트
- 첫 기동에서 확인할 것
- 흔한 실패
- 프로덕션으로 넘어가기 전에
- 마치며 — 순서가 곧 진단이다
- 직접 해보기
- 시리즈
- 참고 자료
들어가며 — 무엇을 띄우는지 알고 시작하기
3편에서 구성 요소를 봤습니다. 웹과 워커 컨테이너, 그리고 Postgres, ClickHouse, Redis, 오브젝트 스토리지입니다. 이번 글은 그것들을 실제로 세우는 쪽입니다.
자체 호스팅에서 시간을 가장 많이 잡아먹는 것은 배포 도구가 아닙니다. 값을 잘못 넣은 환경 변수 하나입니다. 그리고 잘못된 값은 대개 조용히 실패합니다. 컨테이너는 뜨고, 로그인도 되는데, 트레이스만 안 들어옵니다.
구성과 설정 이름은 2026-08-15에 공식 문서에서 확인했습니다. Langfuse는 버전에 따라 아키텍처가 달라지므로 사용 중인 버전의 문서를 다시 확인하세요. 아래 이름과 기본값은 v4 문서 기준입니다. 문서에서 확인하지 못한 값은 이 글에 적지 않았습니다.
가장 빠른 경로 — docker compose
docker compose 문서가 안내하는 절차는 저장소를 받아 compose를 올리는 것입니다.
git clone https://github.com/langfuse/langfuse.git
cd langfuse
# 시크릿을 먼저 바꾼 뒤에 올린다
docker compose up -d
docker compose ps
저장소 최상단의 compose 파일을 2026-08-15에 확인한 기준으로, 정의된 서비스와 이미지는 다음과 같습니다.
| 서비스 | 이미지 | 노출 |
|---|---|---|
| langfuse-web | docker.io/langfuse/langfuse:4 | 3000번을 외부로 |
| langfuse-worker | docker.io/langfuse/langfuse-worker:4 | 3030번을 루프백에만 |
| postgres | docker.io/postgres (기본 태그 17) | 루프백에만 |
| clickhouse | docker.io/clickhouse/clickhouse-server:25.12 | 루프백에만 |
| redis | docker.io/redis:7 | 루프백에만 |
| minio | cgr.dev/chainguard/minio | 9090번을 외부로 |
두 Langfuse 서비스는 네 저장소 모두에 헬스 체크 기반 의존성을 걸고 있습니다. 그래서 저장소가 준비되기 전에는 기동하지 않습니다. 문서는 인스턴스에 보안 그룹이나 방화벽을 두어 3000번과 9090번으로만 들어오게 제한하라고 권고합니다.
compose 파일 안에서 바꿔야 하는 줄에는 # CHANGEME 주석이 붙어 있습니다. 이 주석을 전부 찾아 처리하기 전에는 프로덕션으로 올리면 안 됩니다.
반드시 직접 만들어 넣는 값
설정 문서가 필수로 표시하는 보안 관련 변수는 네 개입니다.
| 변수 | 문서가 설명하는 용도 |
|---|---|
NEXTAUTH_URL | Langfuse 웹 배포의 URL |
NEXTAUTH_SECRET | 로그인 세션 쿠키 검증에 사용 |
SALT | 해시된 API 키에 소금을 치는 데 사용 |
ENCRYPTION_KEY | 민감한 데이터 암호화에 사용 |
문서는 NEXTAUTH_SECRET과 SALT에 256비트 이상의 엔트로피를, ENCRYPTION_KEY에는 16진수 형식의 256비트 값을 요구합니다.
# 예시: 요구되는 형식에 맞는 값을 만든다
openssl rand -base64 32 # NEXTAUTH_SECRET
openssl rand -base64 32 # SALT
openssl rand -hex 32 # ENCRYPTION_KEY (16진수 256비트)
SALT와 ENCRYPTION_KEY는 나중에 바꾸면 기존 데이터를 읽지 못하게 됩니다. 배포 첫날에 정하고 시크릿 관리 시스템에 넣어 두세요. NEXTAUTH_URL은 실제 접속 주소와 정확히 같아야 합니다. 리버스 프록시 뒤에서 이 값이 내부 주소로 남아 있으면 로그인 리다이렉트가 깨집니다.
저장소별 연결 설정
Postgres 쪽에서 문서가 정의하는 변수는 네 개입니다.
DATABASE_URL— Postgres 연결 문자열입니다. 필수입니다.DIRECT_URL— 마이그레이션에 쓰는 연결 문자열입니다. 기본값은DATABASE_URL이며, 마이그레이션 전용 계정이나 풀러를 우회한 직결이 필요할 때 따로 줍니다.SHADOW_DATABASE_URL— 데이터베이스 사용자에게 데이터베이스 생성 권한이 없을 때 필요합니다.LANGFUSE_AUTO_POSTGRES_MIGRATION_DISABLED— 기본값 false이며, 기동 시 자동 마이그레이션을 끕니다.
ClickHouse 쪽은 연결 문자열이 두 개라는 점이 처음 보면 헷갈립니다. 프로토콜이 다르기 때문입니다.
# 예시: 프로토콜이 서로 다른 두 엔드포인트
CLICKHOUSE_MIGRATION_URL="clickhouse://clickhouse-host:9000" # TCP, 9000 또는 9440
CLICKHOUSE_URL="http://clickhouse-host:8123" # HTTP(S), 8123 또는 8443
CLICKHOUSE_USER="langfuse"
CLICKHOUSE_PASSWORD="changeme"
CLICKHOUSE_DB="langfuse"
CLICKHOUSE_CLUSTER_ENABLED="false"
ClickHouse 문서가 밝히는 패턴이 이것입니다. 마이그레이션은 TCP 프로토콜로, 일반 질의는 HTTP로 나갑니다. CLICKHOUSE_DB의 기본값은 default이고, CLICKHOUSE_CLUSTER_ENABLED의 기본값은 true입니다. 단일 컨테이너로 띄웠다면 이 값을 false로 내려야 합니다. 클러스터 이름은 CLICKHOUSE_CLUSTER_NAME으로 바꾸며 기본값은 default입니다. SSL이 필요하면 CLICKHOUSE_MIGRATION_SSL을 켭니다.
사용자 권한은 3편에서 본 그대로입니다. INSERT, SELECT, ALTER UPDATE, ALTER DELETE, ALTER DROP INDEX, CREATE, DROP TABLE이 필요합니다. 이 중 하나라도 빠지면 마이그레이션 단계에서 멈춥니다.
Redis는 REDIS_CONNECTION_STRING 하나로 지정하거나 REDIS_HOST, REDIS_PORT, REDIS_AUTH로 나눠 줍니다. 클러스터와 센티널 모드를 쓴다면 각각 REDIS_CLUSTER_ENABLED와 REDIS_SENTINEL_ENABLED 계열 변수가 따로 있습니다. 그리고 3편에서 강조한 maxmemory-policy를 noeviction으로 두는 설정을 잊지 마세요.
오브젝트 스토리지 설정
오브젝트 스토리지 문서에서 필수로 표시된 것은 이벤트 업로드 버킷입니다. 미디어 업로드 버킷도 설정 문서에서는 필수로 표시됩니다.
# 예시: MinIO 를 쓰는 경우
LANGFUSE_S3_EVENT_UPLOAD_BUCKET="langfuse"
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT="http://minio:9000"
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID="minio"
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY="changeme"
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE="true"
LANGFUSE_S3_EVENT_UPLOAD_PREFIX="events/"
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET="langfuse"
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT="http://minio:9000"
LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE="true"
두 가지를 기억하면 됩니다. 접두사를 줄 때는 반드시 슬래시로 끝나야 합니다. 그리고 MinIO에서는 경로 방식 강제 옵션이 필요하다고 문서가 명시합니다. 이 옵션이 없으면 버킷 이름이 호스트명으로 해석되면서 이름 해석에 실패합니다.
배치 내보내기는 기본값이 꺼져 있습니다. LANGFUSE_S3_BATCH_EXPORT_ENABLED를 켜면 별도의 버킷 설정이 필요해집니다. AWS S3라면 필요한 최소 권한은 버킷과 객체 양쪽에 대한 s3:PutObject, s3:ListBucket, s3:GetObject입니다.
쿠버네티스 경로 — 헬름 차트
헬름 문서가 안내하는 차트 저장소와 설치 명령은 다음과 같습니다.
helm repo add langfuse https://langfuse.github.io/langfuse-k8s
helm repo update
helm install langfuse langfuse/langfuse -n langfuse --create-namespace
기본 설치는 애플리케이션 컨테이너와 데이터 저장소를 함께 올립니다. 이미 운영 중인 Postgres, ClickHouse, Redis를 가리키게 바꿀 수도 있습니다. 차트 저장소의 README가 밝히는 값 구조는 최상위 키가 langfuse, postgresql, clickhouse, redis, 그리고 s3 또는 minio로 나뉘는 형태입니다.
# 예시: 시크릿을 값 파일에 직접 쓰지 않는 형태
langfuse:
salt:
secretKeyRef:
name: langfuse-secrets
key: salt
nextauth:
secret:
secretKeyRef:
name: langfuse-secrets
key: nextauth-secret
encryptionKey:
secretKeyRef:
name: langfuse-secrets
key: encryption-key
postgresql:
auth:
username: langfuse
existingSecret: langfuse-postgres
clickhouse:
auth:
existingSecret: langfuse-clickhouse
redis:
auth:
existingSecret: langfuse-redis
s3:
storageProvider: s3
값의 정확한 중첩 형태는 차트 버전에 따라 달라지므로, 실제 키 이름과 기본값은 사용 중인 버전의 문서와 차트 README에서 확인하세요. README가 명시하는 원칙은 두 가지입니다. 비밀번호는 값으로 직접 쓰거나 existingSecret과 existingSecretKey로 기존 시크릿을 가리킬 수 있고, 외부 데이터 저장소를 쓰면 차트 릴리스와 저장소의 수명 주기를 분리할 수 있습니다.
주의할 점이 하나 있습니다. 릴리스 이름을 langfuse가 아닌 다른 이름으로 설치하면 Redis 호스트명을 그에 맞게 조정해야 한다고 문서가 밝힙니다. 헬름의 이름 조합 규칙 때문에 생기는 문제입니다.
첫 기동에서 확인할 것
헬름 문서는 배포가 최대 5분까지 걸릴 수 있고, 그 사이 langfuse-web과 langfuse-worker 컨테이너가 데이터베이스 준비 과정에서 재시작한다고 밝힙니다. 즉 초기에 보이는 재시작은 정상 동작입니다. 5분이 지나도 반복된다면 그때부터 진짜 문제입니다.
# 컨테이너 상태와 로그
docker compose ps
docker compose logs -f langfuse-web
docker compose logs -f langfuse-worker
# 웹이 응답하는지 확인
curl -sS -o /dev/null -w "%{http_code}\n" http://localhost:3000
확인 순서는 이렇게 잡으면 좋습니다.
- 네 저장소가 모두 healthy 상태인지 봅니다. 여기서 막히면 애플리케이션 로그를 볼 필요가 없습니다.
- 웹 로그에서 마이그레이션이 끝났는지 확인합니다. Postgres와 ClickHouse 양쪽입니다.
- 웹에 접속해 계정을 만들고 프로젝트를 만듭니다. 여기까지는 Postgres만으로 됩니다.
- SDK로 트레이스를 하나 보냅니다. 화면에 뜨면 ClickHouse, Redis, 오브젝트 스토리지가 모두 살아 있다는 뜻입니다.
4번이 3편에서 본 수집 경로를 통째로 검증합니다. 그래서 첫 기동 확인은 여기까지 해야 끝입니다.
흔한 실패
문서에 근거가 있는 실패 유형을 모으면 다음과 같습니다.
| 증상 | 원인 | 확인할 것 |
|---|---|---|
| 질의가 빈 결과를 반환 | 인프라 구성 요소가 UTC가 아님 | 모든 저장소 컨테이너의 시간대 |
| 마이그레이션 단계에서 정지 | ClickHouse 사용자 권한 부족 | 3편의 GRANT 목록 |
| 마이그레이션이 CREATE DATABASE 실패 | Postgres 사용자 권한 부족 | SHADOW_DATABASE_URL |
| 큐 이벤트가 사라짐 | maxmemory-policy가 noeviction이 아님 | Redis 설정 |
| 버킷 접근 실패 | 경로 방식 옵션 누락 | LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE |
| 컨테이너가 메모리로 죽음 | Node 힙 상한 미설정 | NODE_OPTIONS |
| 로그인 리다이렉트가 깨짐 | 외부 주소와 불일치 | NEXTAUTH_URL |
마지막에서 두 번째 항목은 컨테이너 문서가 명시하는 사항입니다. 컨테이너에 할당한 메모리가 Node의 기본 상한인 약 1.7GiB를 넘을 때, NODE_OPTIONS로 힙 크기를 명시하지 않으면 문제가 생깁니다. 두 컨테이너 모두에 설정해야 합니다.
기동 문제를 추적할 때는 로그 설정도 도움이 됩니다. LANGFUSE_LOG_LEVEL의 기본값은 info이고 trace부터 fatal까지 조절할 수 있습니다. LANGFUSE_LOG_FORMAT은 기본 text이며 json으로 바꾸면 로그 수집 파이프라인에 넣기 쉬워집니다.
프로덕션으로 넘어가기 전에
컨테이너 문서가 권고하는 자원 배분은 모든 컨테이너에 최소 CPU 2코어와 메모리 4GB입니다. 가용성을 위해 웹 컨테이너는 최소 두 개를 띄우고, 어느 쪽이든 CPU 사용률이 50퍼센트를 넘으면 인스턴스를 늘리라고 합니다.
저장소 쪽 권고는 3편에서 정리한 대로입니다. ClickHouse는 샤드 1개에 복제본 3개 이상, Postgres는 v4에서 최소 15와 16 권장, Redis는 7.2 권장입니다.
마이그레이션 정책도 프로덕션에서는 다시 생각할 필요가 있습니다. 기본값은 기동 시 자동 마이그레이션입니다. 여러 인스턴스가 동시에 뜨는 환경이나 스키마 변경을 배포와 분리하고 싶은 경우에는 LANGFUSE_AUTO_POSTGRES_MIGRATION_DISABLED와 LANGFUSE_AUTO_CLICKHOUSE_MIGRATION_DISABLED를 켜고 별도 단계로 돌립니다. ClickHouse 문서는 수동 마이그레이션 절차로 저장소를 받아 ./packages/shared/clickhouse/migrations/clustered/ 아래의 SQL에서 클러스터 이름을 조정한 뒤 실행하라고 안내합니다.
마치며 — 순서가 곧 진단이다
배포 순서를 저장소부터 애플리케이션으로 잡으면 문제가 생겨도 범위가 좁습니다. 저장소 넷을 먼저 세워 healthy를 확인하고, 시크릿 네 개를 만들어 넣고, 연결 문자열을 채우고, 그다음 애플리케이션을 올립니다. 마지막으로 트레이스를 한 건 보내 전체 경로를 검증합니다.
지금 할 수 있는 점검은 시크릿 관리입니다. SALT와 ENCRYPTION_KEY가 compose 파일이나 값 파일에 평문으로 남아 있다면, 그것을 시크릿 저장소로 옮기는 일이 다음 배포보다 먼저입니다. 다음 글에서는 이렇게 세운 시스템이 트래픽을 받기 시작할 때 비용이 어디서 늘어나는지를 봅니다.
직접 해보기
- HTTP 요청 빌더 (cURL 생성기) — 첫 기동 확인용 헬스 체크 요청을 만들어 두면 배포 스크립트에 그대로 넣을 수 있습니다.
- PostgreSQL 놀이터 — 마이그레이션 계정과 애플리케이션 계정의 권한을 나누는 연습을 해 보세요.
시리즈
참고 자료
- Langfuse docker compose 배포: https://langfuse.com/self-hosting/deployment/docker-compose
- Langfuse 쿠버네티스 헬름 배포: https://langfuse.com/self-hosting/deployment/kubernetes-helm
- Langfuse 설정 변수: https://langfuse.com/self-hosting/configuration
- Langfuse ClickHouse: https://langfuse.com/self-hosting/infrastructure/clickhouse
- Langfuse 오브젝트 스토리지: https://langfuse.com/self-hosting/infrastructure/blobstorage
- Langfuse 컨테이너: https://langfuse.com/self-hosting/infrastructure/containers
- langfuse-k8s 차트 저장소: https://github.com/langfuse/langfuse-k8s
Self-Hosting Langfuse — Deployment Paths, Secrets, and What Catches You on First Boot
- Opening — Know What You Are Starting
- The Fastest Path — docker compose
- The Values You Must Generate Yourself
- Connection Settings Per Datastore
- Object Storage Configuration
- The Kubernetes Path — the Helm Chart
- What to Check on First Boot
- Common Failures
- Before Moving to Production
- Closing — The Order Is the Diagnosis
- Try It Yourself
- Series
- References
Opening — Know What You Are Starting
The third post covered the components: the web and worker containers, plus Postgres, ClickHouse, Redis, and object storage. This post is about actually standing them up.
What eats the most time in self-hosting is not the deployment tool. It is one environment variable with the wrong value. And wrong values usually fail quietly: containers come up, login works, and only the traces never arrive.
Component and configuration names here were verified against the official documentation on 2026-08-15. Langfuse's architecture differs by version, so re-check the docs for the version you are running. The names and defaults below are from the v4 documentation. Values I could not confirm in the docs are not written here.
The Fastest Path — docker compose
The docker compose documentation walks you through cloning the repository and bringing compose up.
git clone https://github.com/langfuse/langfuse.git
cd langfuse
# change the secrets first, then bring it up
docker compose up -d
docker compose ps
Reading the compose file at the top of the repository on 2026-08-15, the defined services and images are as follows.
| Service | Image | Exposure |
|---|---|---|
| langfuse-web | docker.io/langfuse/langfuse:4 | 3000 published externally |
| langfuse-worker | docker.io/langfuse/langfuse-worker:4 | 3030 bound to loopback |
| postgres | docker.io/postgres (default tag 17) | loopback only |
| clickhouse | docker.io/clickhouse/clickhouse-server:25.12 | loopback only |
| redis | docker.io/redis:7 | loopback only |
| minio | cgr.dev/chainguard/minio | 9090 published externally |
Both Langfuse services declare health-check-based dependencies on all four datastores, so they do not start before the stores are ready. The documentation recommends putting a security group or firewall on the instance restricting incoming traffic to ports 3000 and 9090.
Lines in the compose file that need changing carry a # CHANGEME comment. Do not put this in production before every one of them is handled.
The Values You Must Generate Yourself
The configuration documentation marks four security-related variables as required.
| Variable | Purpose as documented |
|---|---|
NEXTAUTH_URL | URL of your Langfuse web deployment |
NEXTAUTH_SECRET | Used to validate login session cookies |
SALT | Used to salt hashed API keys |
ENCRYPTION_KEY | Used to encrypt sensitive data |
The documentation asks for 256 or more bits of entropy for NEXTAUTH_SECRET and SALT, and a 256-bit value in hex format for ENCRYPTION_KEY.
# Illustrative: generate values matching the required formats
openssl rand -base64 32 # NEXTAUTH_SECRET
openssl rand -base64 32 # SALT
openssl rand -hex 32 # ENCRYPTION_KEY (256-bit hex)
Change SALT or ENCRYPTION_KEY later and you lose the ability to read existing data. Decide them on day one and put them in a secrets manager. NEXTAUTH_URL has to match the address people actually reach, exactly. Behind a reverse proxy, leaving it as an internal address breaks the login redirect.
Connection Settings Per Datastore
On the Postgres side the documentation defines four variables.
DATABASE_URL— the Postgres connection string. Required.DIRECT_URL— the connection string used for database migrations. Defaults toDATABASE_URL; set it separately when you want a migration-specific user or a direct connection that bypasses a pooler.SHADOW_DATABASE_URL— required when the database user lacks CREATE DATABASE permission.LANGFUSE_AUTO_POSTGRES_MIGRATION_DISABLED— defaults to false; disables automatic migrations on startup.
On the ClickHouse side, having two connection strings is confusing the first time. The reason is that the protocols differ.
# Illustrative: two endpoints on different protocols
CLICKHOUSE_MIGRATION_URL="clickhouse://clickhouse-host:9000" # TCP, 9000 or 9440
CLICKHOUSE_URL="http://clickhouse-host:8123" # HTTP(S), 8123 or 8443
CLICKHOUSE_USER="langfuse"
CLICKHOUSE_PASSWORD="changeme"
CLICKHOUSE_DB="langfuse"
CLICKHOUSE_CLUSTER_ENABLED="false"
That is the pattern the ClickHouse documentation gives. Migrations go over the TCP protocol, ordinary queries over HTTP. CLICKHOUSE_DB defaults to default, and CLICKHOUSE_CLUSTER_ENABLED defaults to true — bring it down to false if you started a single container. The cluster name is set with CLICKHOUSE_CLUSTER_NAME, defaulting to default. If you need SSL, turn on CLICKHOUSE_MIGRATION_SSL.
User permissions are exactly as covered in the third post: INSERT, SELECT, ALTER UPDATE, ALTER DELETE, ALTER DROP INDEX, CREATE, and DROP TABLE. Miss any one of them and it stops at the migration step.
Redis is configured either with REDIS_CONNECTION_STRING alone or split into REDIS_HOST, REDIS_PORT, and REDIS_AUTH. For cluster and sentinel modes there are separate REDIS_CLUSTER_ENABLED and REDIS_SENTINEL_ENABLED families of variables. And do not forget the maxmemory-policy set to noeviction that the third post emphasized.
Object Storage Configuration
The object storage documentation marks the event upload bucket as required. The configuration page marks the media upload bucket as required too.
# Illustrative: using MinIO
LANGFUSE_S3_EVENT_UPLOAD_BUCKET="langfuse"
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT="http://minio:9000"
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID="minio"
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY="changeme"
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE="true"
LANGFUSE_S3_EVENT_UPLOAD_PREFIX="events/"
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET="langfuse"
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT="http://minio:9000"
LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE="true"
Two things to remember. A prefix must end with a slash. And the documentation states that MinIO requires the force-path-style option; without it the bucket name is interpreted as a hostname and name resolution fails.
Batch export is off by default. Turning on LANGFUSE_S3_BATCH_EXPORT_ENABLED brings its own bucket configuration with it. On AWS S3 the minimum permissions are s3:PutObject, s3:ListBucket, and s3:GetObject on both the bucket and its objects.
The Kubernetes Path — the Helm Chart
The Helm documentation gives this chart repository and install command.
helm repo add langfuse https://langfuse.github.io/langfuse-k8s
helm repo update
helm install langfuse langfuse/langfuse -n langfuse --create-namespace
The default install brings up both application containers and the data stores. You can also point it at Postgres, ClickHouse, and Redis you already operate. The chart repository README describes the values structure as top-level keys langfuse, postgresql, clickhouse, redis, and s3 or minio.
# Illustrative: keeping secrets out of the values file
langfuse:
salt:
secretKeyRef:
name: langfuse-secrets
key: salt
nextauth:
secret:
secretKeyRef:
name: langfuse-secrets
key: nextauth-secret
encryptionKey:
secretKeyRef:
name: langfuse-secrets
key: encryption-key
postgresql:
auth:
username: langfuse
existingSecret: langfuse-postgres
clickhouse:
auth:
existingSecret: langfuse-clickhouse
redis:
auth:
existingSecret: langfuse-redis
s3:
storageProvider: s3
The exact nesting of these values differs by chart version, so confirm the real key names and defaults in the documentation for the version you are running and in the chart README. The README states two principles: passwords can be given directly or referenced through existingSecret and existingSecretKey, and using external data stores lets you decouple the datastore lifecycle from chart releases.
One thing to watch for. The documentation notes that if you install the chart under a release name other than langfuse, the Redis hostname has to be adjusted accordingly — a consequence of Helm's naming rules.
What to Check on First Boot
The Helm documentation says deployment can take up to five minutes and that the langfuse-web and langfuse-worker containers restart during database provisioning. In other words, the restarts you see early on are normal. If they keep repeating past five minutes, that is when it becomes a real problem.
# container state and logs
docker compose ps
docker compose logs -f langfuse-web
docker compose logs -f langfuse-worker
# check that web responds
curl -sS -o /dev/null -w "%{http_code}\n" http://localhost:3000
A good order to check things in:
- Confirm all four datastores are healthy. If you are stuck here, there is no point reading application logs.
- Confirm in the web logs that migrations finished — both Postgres and ClickHouse.
- Open the web UI, create an account, create a project. This far works with Postgres alone.
- Send one trace with the SDK. If it shows up, ClickHouse, Redis, and object storage are all alive.
Step four validates the entire ingestion path from the third post in one shot. That is why first-boot verification is not finished until you have done it.
Common Failures
Collecting the failure modes that have a basis in the documentation:
| Symptom | Cause | What to check |
|---|---|---|
| Queries return empty results | An infrastructure component is not on UTC | Timezone of every datastore container |
| Stops at the migration step | ClickHouse user lacks grants | The GRANT list from the third post |
| Migration fails on CREATE DATABASE | Postgres user lacks permission | SHADOW_DATABASE_URL |
| Queued events disappear | maxmemory-policy is not noeviction | Redis configuration |
| Bucket access fails | Path style option missing | LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE |
| Container dies on memory | Node heap ceiling not set | NODE_OPTIONS |
| Login redirect breaks | Mismatch with the external address | NEXTAUTH_URL |
The second-to-last item comes from the containers documentation. When the memory allocated to a container exceeds Node's default ceiling of roughly 1.7 GiB, not declaring the heap size through NODE_OPTIONS causes problems. It has to be set on both containers.
Log settings help when chasing startup problems. LANGFUSE_LOG_LEVEL defaults to info and ranges from trace to fatal. LANGFUSE_LOG_FORMAT defaults to text; switching it to json makes the logs easy to feed into a collection pipeline.
Before Moving to Production
The containers documentation recommends at least 2 CPUs and 4 GB of RAM for all containers. Run at least two web containers for availability, and add instances when CPU utilization exceeds 50 percent on either container.
Datastore recommendations are as summarized in the third post: ClickHouse with one shard and three or more replicas, Postgres at minimum 15 with 16 recommended for v4, and Redis 7.2 recommended.
Migration policy deserves a second look in production too. The default is automatic migration on startup. In environments where several instances come up at once, or when you want schema changes separated from deployment, turn on LANGFUSE_AUTO_POSTGRES_MIGRATION_DISABLED and LANGFUSE_AUTO_CLICKHOUSE_MIGRATION_DISABLED and run them as their own step. The ClickHouse documentation describes the manual procedure as cloning the repository, adjusting cluster names in the SQL under ./packages/shared/clickhouse/migrations/clustered/, and running them.
Closing — The Order Is the Diagnosis
Deploy datastores first and applications second and the blast radius of any problem stays small. Stand up the four stores and confirm healthy, generate the four secrets, fill in the connection strings, then bring up the application. Finally, send one trace to validate the whole path.
The check you can run today is on secrets management. If SALT and ENCRYPTION_KEY are sitting in plain text in a compose file or a values file, moving them into a secrets store comes before your next deployment. The next post looks at where costs grow once this system starts taking real traffic.
Try It Yourself
- HTTP Request Builder — build the first-boot health check request once and drop it straight into your deployment script.
- PostgreSQL Playground — practise splitting permissions between a migration account and an application account.
Series
- Previous: Why Langfuse Puts Traces in ClickHouse
- Next: When Traces Become Cost — Retention, Sampling, and Masking
References
- Langfuse docker compose deployment: https://langfuse.com/self-hosting/deployment/docker-compose
- Langfuse Kubernetes Helm deployment: https://langfuse.com/self-hosting/deployment/kubernetes-helm
- Langfuse configuration variables: https://langfuse.com/self-hosting/configuration
- Langfuse ClickHouse: https://langfuse.com/self-hosting/infrastructure/clickhouse
- Langfuse blob storage: https://langfuse.com/self-hosting/infrastructure/blobstorage
- Langfuse containers: https://langfuse.com/self-hosting/infrastructure/containers
- langfuse-k8s chart repository: https://github.com/langfuse/langfuse-k8s