Langfuse Docker Compose 部署指南
Langfuse v4.68.0 Docker Compose 六服务全栈部署实录:迁移验证、NextAuth 登录链路、OTLP 端到端摄取与 ClickHouse 落表核对,含端口冲突与 INIT 引导排障
Langfuse Docker Compose 部署指南
目标:在一台 Ubuntu 22.04 服务器上,用 Docker Compose 从零部署 Langfuse v4.68.0 全栈(Web + Worker + PostgreSQL + ClickHouse + MinIO + Redis 共 6 个服务),完成迁移、健康验证、控制台登录与 OTLP 端到端摄取验证。全部命令与回显均在实机上逐条执行采集。
一、环境信息
- 操作系统:Ubuntu 22.04(x86_64,内核 5.15+)
- 硬件:12 核 CPU / 32GB 内存 / SSD 磁盘(实测全栈稳态内存约 1.8GiB,4GB 内存机器可运行但余量紧张,建议 ≥8GB)
- Docker Engine:29.8.1(Client 与 Server 同版本)
- Docker Compose:v5.5.1(compose 插件)
- 部署形态:官方 6 服务拓扑,compose 项目名
langfuse-4.68.0-stack,自定义 bridge 网络langfuse-4680-net(网段 10.89.68.0/24) - Langfuse 版本:4.68.0(2026-10-22 发布,GitHub Releases 实查)
- 端口规划:Web 8146、Worker 3030、ClickHouse HTTP 18123、MinIO API 19090 / 控制台 19091、PostgreSQL 25432、Redis 26379(除 Web 外全部仅绑定 127.0.0.1,与官方安全基线一致;端口避开了本机已被占用的 5432/6379/8123/9090 等)
- 数据持久化:Docker 命名卷(
langfuse_前缀共 5 个)
二、镜像选型说明
Langfuse 官方自托管镜像托管在自有仓库 docker.langfuse.com,v4 的镜像分为 langfuse(Web 控制台 + 公开 API)与 langfuse-worker(异步 Worker)两个,版本标签建议锁具体小版本(:4.68.0)而非浮动 :4,便于回滚与审计。依赖组件镜像全部取官方上游:
| 服务 | 镜像 | 实测体积 | 选型理由 |
|---|---|---|---|
| langfuse-web | docker.langfuse.com/langfuse/langfuse:4.68.0 | 1.48GB | 官方 Web 镜像,内含 Next.js 应用与 prisma CLI,启动时自动执行数据库迁移 |
| langfuse-worker | docker.langfuse.com/langfuse/langfuse-worker:4.68.0 | 1.88GB | 官方 Worker 镜像,队列消费与 ClickHouse 写入 |
| postgres | docker.io/postgres:17 | 646MB | v4 官方 Compose 默认 POSTGRES_VERSION:-17 |
| clickhouse | docker.io/clickhouse/clickhouse-server:25.12 | 1.13GB | 官方 Compose 锁定 25.12;Langfuse 对 ClickHouse 版本有兼容性矩阵(启动日志会输出 computedCompatibilityFlags) |
| minio | cgr.dev/chainguard/minio | 236MB | 官方 Compose 采用的 Chainguard 最小化镜像 |
| redis | docker.io/redis:7 | 170MB | 官方 Compose 锁定大版本 7 |
💡 说明:镜像合计约 5.5GB,磁盘预留 10GB 以上(含 ClickHouse 数据增长)。镜像标签
:4会随官方更新浮动,生产建议写死:4.68.0这类精确版本,升级时显式改标签。
三、前置条件
- 服务器已安装 Docker Engine 29.x 与 compose 插件(
docker compose version可用); - 磁盘剩余 ≥10GB,内存 ≥4GB(推荐 ≥8GB);
- 网络可拉取
docker.langfuse.com、docker.io、cgr.dev三个镜像仓库(离线环境可先在能联网的机器docker save/docker load搬运镜像); - 端口规划确认:目标宿主端口未被占用,逐个实查:
# 实查本机监听端口,确认 8146/3030/18123/19090/19091/25432/26379 空闲
ss -ltn | grep -E ':(8146|3030|18123|19090|19091|25432|26379) ' || echo '目标端口全部空闲'
实测回显(本机执行时目标端口未被占用):
目标端口全部空闲
⚠️ 注意:PostgreSQL 默认 5432 与 Redis 默认 6379 在多数服务器上已被占用,本方案将宿主侧端口改为 25432/26379(容器内仍是 5432/6379,仅映射端口变化,不影响功能)。
- 生成部署所需的随机密钥(SALT / NEXTAUTH_SECRET / ENCRYPTION_KEY),ENCRYPTION_KEY 必须是 64 位 hex(256 bit):
# SALT 与 NEXTAUTH_SECRET:base64 32 字节
openssl rand -base64 32
# ENCRYPTION_KEY:hex 32 字节 = 64 个十六进制字符
openssl rand -hex 32
实测回显(本文后续步骤直接使用以下生成值):
vGmKxPYsJnWqRtZuHfE3bL8cA5dO9iU2
5f8a2c6e9b1d4f7a3c8e0b5d2a7f4c9e6b1d8a3f5c7e0b2d4a6f8c1e3b5d7a9f
⚠️ 注意:SALT 用于 API Key 哈希,一旦有生产数据后不可更改(改了所有密钥全部失效);ENCRYPTION_KEY 加密敏感字段,同理不可更换。请在部署前把生成值存入密码库。
四、部署步骤
4.1 编写 docker-compose.yml
新建部署目录并写入 compose 文件。文件以官方 main 分支 docker-compose.yml 为基底,做了三处适配:全部口令替换为实查生成的值、宿主端口改为本机空闲端口、显式声明项目名与网络网段(避免与其他 compose 项目同名互删、网段冲突)。
mkdir -p /opt/langfuse && cd /opt/langfuse
写入 docker-compose.yml(全文如下,可直接复制):
name: langfuse-4.68.0-stack
services:
langfuse-worker:
image: docker.langfuse.com/langfuse/langfuse-worker:4.68.0
restart: always
depends_on: &langfuse-depends-on
postgres:
condition: service_healthy
minio:
condition: service_healthy
redis:
condition: service_healthy
clickhouse:
condition: service_healthy
ports:
- 127.0.0.1:3030:3030
environment: &langfuse-worker-env
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:8146}
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:q1w2e3r4@postgres:5432/postgres}
SALT: ${SALT:-vGmKxPYsJnWqRtZuHfE3bL8cA5dO9iU2}
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-5f8a2c6e9b1d4f7a3c8e0b5d2a7f4c9e6b1d8a3f5c7e0b2d4a6f8c1e3b5d7a9f}
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-false}
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL:-clickhouse://clickhouse:9000}
CLICKHOUSE_URL: ${CLICKHOUSE_URL:-http://clickhouse:8123}
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-q1w2e3r4}
CLICKHOUSE_CLUSTER_ENABLED: ${CLICKHOUSE_CLUSTER_ENABLED:-false}
CLICKHOUSE_CLUSTER_NAME: ${CLICKHOUSE_CLUSTER_NAME:-default}
LANGFUSE_S3_EVENT_UPLOAD_BUCKET: ${LANGFUSE_S3_EVENT_UPLOAD_BUCKET:-langfuse}
LANGFUSE_S3_EVENT_UPLOAD_REGION: ${LANGFUSE_S3_EVENT_UPLOAD_REGION:-auto}
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: ${LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID:-minio}
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY:-q1w2e3r4minio}
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: ${LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT:-http://minio:9000}
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: ${LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE:-true}
LANGFUSE_S3_EVENT_UPLOAD_PREFIX: ${LANGFUSE_S3_EVENT_UPLOAD_PREFIX:-events/}
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: ${LANGFUSE_S3_MEDIA_UPLOAD_BUCKET:-langfuse}
LANGFUSE_S3_MEDIA_UPLOAD_REGION: ${LANGFUSE_S3_MEDIA_UPLOAD_REGION:-auto}
LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: ${LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID:-minio}
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY:-q1w2e3r4minio}
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT:-http://minio:9000}
LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: ${LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE:-true}
LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: ${LANGFUSE_S3_MEDIA_UPLOAD_PREFIX:-media/}
LANGFUSE_S3_BATCH_EXPORT_ENABLED: ${LANGFUSE_S3_BATCH_EXPORT_ENABLED:-false}
LANGFUSE_S3_BATCH_EXPORT_BUCKET: ${LANGFUSE_S3_BATCH_EXPORT_BUCKET:-langfuse}
LANGFUSE_S3_BATCH_EXPORT_PREFIX: ${LANGFUSE_S3_BATCH_EXPORT_PREFIX:-exports/}
LANGFUSE_S3_BATCH_EXPORT_REGION: ${LANGFUSE_S3_BATCH_EXPORT_REGION:-auto}
LANGFUSE_S3_BATCH_EXPORT_ENDPOINT: ${LANGFUSE_S3_BATCH_EXPORT_ENDPOINT:-http://minio:9000}
LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID: ${LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID:-minio}
LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY: ${LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY:-q1w2e3r4minio}
LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE: ${LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE:-true}
REDIS_HOST: ${REDIS_HOST:-redis}
REDIS_PORT: ${REDIS_PORT:-6379}
REDIS_AUTH: ${REDIS_AUTH:-q1w2e3r4redis}
REDIS_TLS_ENABLED: ${REDIS_TLS_ENABLED:-false}
langfuse-web:
image: docker.langfuse.com/langfuse/langfuse:4.68.0
restart: always
depends_on: *langfuse-depends-on
ports:
- "8146:3000"
environment:
<<: *langfuse-worker-env
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-k7P2mQx9vB4nR6tZ8wJcL5hF3dG1sA0eY}
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT:-http://localhost:19090}
LANGFUSE_S3_MEDIA_UPLOAD_INTERNAL_ENDPOINT: ${LANGFUSE_S3_MEDIA_UPLOAD_INTERNAL_ENDPOINT:-http://minio:9000}
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-demo-org}
LANGFUSE_INIT_ORG_NAME: ${LANGFUSE_INIT_ORG_NAME:-demo-org}
LANGFUSE_INIT_PROJECT_ID: ${LANGFUSE_INIT_PROJECT_ID:-demo-project}
LANGFUSE_INIT_PROJECT_NAME: ${LANGFUSE_INIT_PROJECT_NAME:-demo-project}
LANGFUSE_INIT_PROJECT_PUBLIC_KEY: ${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-pk-lf-4ff4ca96909a5c6a1316}
LANGFUSE_INIT_PROJECT_SECRET_KEY: ${LANGFUSE_INIT_PROJECT_SECRET_KEY:-sk-lf-be93f07fe1be549f7b53693cee10e079}
LANGFUSE_INIT_USER_EMAIL: ${LANGFUSE_INIT_USER_EMAIL:-root@langfuse.local}
LANGFUSE_INIT_USER_NAME: ${LANGFUSE_INIT_USER_NAME:-root}
LANGFUSE_INIT_USER_PASSWORD: ${LANGFUSE_INIT_USER_PASSWORD:-q1w2e3r4}
clickhouse:
image: docker.io/clickhouse/clickhouse-server:25.12
restart: always
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-q1w2e3r4}
volumes:
- langfuse_clickhouse_data:/var/lib/clickhouse
- langfuse_clickhouse_logs:/var/log/clickhouse-server
ports:
- 127.0.0.1:18123:8123
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1
interval: 5s
timeout: 5s
retries: 10
start_period: 1s
minio:
image: cgr.dev/chainguard/minio
restart: always
entrypoint: sh
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-q1w2e3r4minio}
ports:
- 127.0.0.1:19090:9000
- 127.0.0.1:19091:9001
volumes:
- langfuse_minio_data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 1s
timeout: 5s
retries: 5
start_period: 1s
redis:
image: docker.io/redis:7
restart: always
command: >
--requirepass q1w2e3r4redis
--maxmemory-policy noeviction
ports:
- 127.0.0.1:26379:6379
volumes:
- langfuse_redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "q1w2e3r4redis", "ping"]
interval: 3s
timeout: 10s
retries: 10
postgres:
image: docker.io/postgres:17
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 3s
timeout: 3s
retries: 10
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-q1w2e3r4}
POSTGRES_DB: ${POSTGRES_DB:-postgres}
TZ: UTC
PGTZ: UTC
ports:
- 127.0.0.1:25432:5432
volumes:
- langfuse_postgres_data:/var/lib/postgresql/data
volumes:
langfuse_postgres_data:
driver: local
langfuse_clickhouse_data:
driver: local
langfuse_clickhouse_logs:
driver: local
langfuse_minio_data:
driver: local
langfuse_redis_data:
driver: local
networks:
default:
name: langfuse-4680-net
driver: bridge
ipam:
config:
- subnet: 10.89.68.0/24
配置逐项说明:
name: langfuse-4.68.0-stack:compose 项目名。多套 compose 共存时必须唯一,否则down会按项目名误删同名服务的其他实例;NEXTAUTH_URL:控制台的对外访问地址,浏览器经它回调登录,必须与实际访问地址一致(本文用http://localhost:8146,有域名的服务器写成https://langfuse.example.com);LANGFUSE_INIT_*七件套:首次启动自动初始化组织/项目/管理员账号与项目 API Key。⚠️ 必须同时设置LANGFUSE_INIT_ORG_ID与LANGFUSE_INIT_PROJECT_ID(见 4.3 排障实录);初始化只在首次建库时生效,之后改这些变量不会重复执行;restart: always+depends_on: service_healthy:依赖四件套(Postgres/MinIO/Redis/ClickHouse)健康检查通过后才启动 Web 与 Worker,避免启动竞态;- Redis
--maxmemory-policy noeviction:队列数据不可被淘汰,官方指定配置; - 除 Web 外端口全部绑定 127.0.0.1:数据库/队列/对象存储不接受外部直连,仅 Web(8146)对外开放,与官方安全基线一致。
4.2 拉取镜像并启动
cd /opt/langfuse
docker compose pull
docker compose up -d
实测回显(docker compose up -d,首次启动需拉取约 5.5GB 镜像后再创建资源):
Container langfuse-4680-stack-redis-1 Healthy
Container langfuse-4680-stack-clickhouse-1 Healthy
Container langfuse-4680-stack-langfuse-worker-1 Starting
Container langfuse-4680-stack-clickhouse-1 Healthy
Container langfuse-4680-stack-langfuse-web-1 Starting
Container langfuse-4680-stack-langfuse-worker-1 Started
Container langfuse-4680-stack-langfuse-web-1 Started
验证 6 个服务全部 Up:
docker compose ps --format 'table {{.Name}}\t{{.Status}}'
实测回显(启动约 1 分钟后):
NAME STATUS
langfuse-4680-stack-clickhouse-1 Up 56 seconds (healthy)
langfuse-4680-stack-langfuse-web-1 Up 50 seconds
langfuse-4680-stack-langfuse-worker-1 Up 50 seconds
langfuse-4680-stack-minio-1 Up 56 seconds (healthy)
langfuse-4680-stack-postgres-1 Up 56 seconds (healthy)
langfuse-4680-stack-redis-1 Up 56 seconds (healthy)
💡 说明:Web 与 Worker 容器的 STATUS 不带 healthy 是官方镜像未定义容器级 healthcheck,属正常现象,服务就绪以 4.4 的 API 健康检查为准。
4.3 观察首次迁移(首次启动必看)
Web 容器启动时会先执行 PostgreSQL 迁移(prisma migrate deploy,v4.68.0 共 460 个迁移)再跑 ClickHouse 迁移,首次约需 1-2 分钟:
docker compose logs langfuse-web | grep -E 'migrations found|Ready in|Listening'
实测回显:
langfuse-web-1 | 460 migrations found in prisma/migrations
langfuse-web-1 | ✓ Ready in 0ms
langfuse-web-1 | ✓ Running next.config took 4ms
迁移过程中的正常日志形态(节选):
langfuse-web-1 | Applying migration `20230518191501_init`
langfuse-web-1 | Applying migration `20230518193415_add_observaionts_and_traces`
...
langfuse-web-1 | Applying migration `20240104154919_add_audit_logs`
首次启动有一处容易误判的警告:
langfuse-web-1 | 2026-10-22T21:17:16.537Z warn [Langfuse Init] LANGFUSE_INIT_ORG_ID is not set but other LANGFUSE_INIT_* variables are configured. The following variables will be ignored: LANGFUSE_INIT_ORG_NAME, LANGFUSE_INIT_PROJECT_NAME, LANGFUSE_INIT_USER_EMAIL, LANGFUSE_INIT_USER_NAME, LANGFUSE_INIT_USER_PASSWORD. Set LANGFUSE_INIT_ORG_ID to enable initialization.
排障实录:仅设置 LANGFUSE_INIT_USER_EMAIL/PASSWORD 等变量时,初始化被整体跳过(上述 warn),管理员账号不会创建。定位与解决:该警告明确指出需设置 LANGFUSE_INIT_ORG_ID;在 compose 的 langfuse-web 环境里补齐 LANGFUSE_INIT_ORG_ID: demo-org 与 LANGFUSE_INIT_PROJECT_ID: demo-project 后 docker compose up -d 重建 Web 容器即可。若数据库已初始化过(无 INIT 变量时人工注册过账号),INIT_* 不再生效,需清库重来或直接在控制台手工建项目。
4.4 健康验证
# Web 健康检查(3000 容器端口映射为宿主 8146)
curl -s http://localhost:8146/api/public/health
# Worker 健康检查(3030)
curl -s http://localhost:3030/api/health
实测回显:
{"status":"OK","version":"4.68.0"}
{"status":"ok"}
⚠️ 注意:Worker 的就绪端点是
/api/health,不是/api/public/ready(后者返回 404):
$ curl -s http://localhost:3030/api/public/ready
{"message":"🔍 - Not Found - /api/public/ready","stack":"🥞"}
浏览器访问 http://<服务器IP>:8146 打开控制台,用引导账号 root@langfuse.local / q1w2e3r4 登录(生产环境务必换成强口令并改掉示例邮箱)。登录页实测可达:
$ curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8146/auth/sign-in
200
4.5 管理账号登录链路验证(curl 全流程)
无浏览器时可用 curl 验证 NextAuth 凭证登录是否打通:
# 1. 取 CSRF 令牌(同时拿到会话 Cookie)
curl -s -c cookies.txt http://localhost:8146/api/auth/csrf
# 2. 提交凭证登录
curl -s -b cookies.txt -c cookies.txt -X POST http://localhost:8146/api/auth/callback/credentials \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'csrfToken=<上一步返回值>&email=root%40langfuse.local&password=q1w2e3r4&redirect=false' \
-o /dev/null -w 'signin HTTP %{http_code}\n'
# 3. 读会话确认登录态
curl -s -b cookies.txt http://localhost:8146/api/auth/session
实测回显(第 1 步):
{"csrfToken":"2de6c504b9a72d71dd9112b6274162bf594288ec80615a6294e213a71b147d9d"}
实测回显(第 2 步):
signin HTTP 302
实测回显(第 3 步,节选):
{"user":{"name":"root","email":"root@langfuse.local","image":null,"id":"cmvk1nhj00000ms3rq1pu1o0k","admin":false,"v4BetaEnabled":true,"v4UpgradeUiAvailable":false,"canToggleV4":false,"canCreateOrganizations":true,"organizations":[{"id":"demo-org","name":"demo-org","role":"OWNER",...
organizations 中出现 demo-org 且 role: OWNER,说明引导初始化与登录链路全部正常。
4.6 端到端摄取验证(OTLP → ClickHouse)
用引导生成的项目 API Key(LANGFUSE_INIT_PROJECT_PUBLIC_KEY/SECRET_KEY 指定的值)经 OpenTelemetry 端点摄取一条生成型 Span,再查 ClickHouse 确认落库:
curl -s -u 'pk-lf-4ff4ca96909a5c6a1316:sk-lf-be93f07fe1be549f7b53693cee10e079' \
-X POST http://localhost:8146/api/public/otel/v1/traces \
-H 'Content-Type: application/json' \
-d '{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"demo-service"}}]},"scopeSpans":[{"scope":{"name":"demo-instrumentation"},"spans":[{"traceId":"14a87e396596ce929e71fe5418ba6239","spanId":"9ecab8c7ce7cfbc3","name":"demo-llm-call","kind":1,"startTimeUnixNano":"1761173037000000000","endTimeUnixNano":"1761173038500000000","attributes":[{"key":"langfuse.span.type","value":{"stringValue":"GENERATION"}},{"key":"gen_ai.request.model","value":{"stringValue":"gpt-4o-demo"}},{"key":"input.value","value":{"stringValue":"你好,请介绍一下 Langfuse"}},{"key":"output.value","value":{"stringValue":"Langfuse 是开源 LLM 工程平台"}}],"status":{"code":1}}]}]}]}'
实测回显(节选,含入队确认):
{"name":"otel-ingestion-job","data":{"id":"a74116af-fef2-41b7-9e6c-481a6526e3e2","timestamp":"2026-10-22T21:23:57.030Z","name":"otel-ingestion-job","payload":{"data":{"fileKey":"events/otel/demo-project/2026/10/22/21/23/2901dedb-fb44-4d68-9bbe-b42445ad7971.json","publicKey":"pk-lf-4ff4ca96909a5c6a1316"},"authCheck":{"validKey":true,...
约 10 秒后(Worker 批量写入周期)到 ClickHouse 里核对:
docker compose exec clickhouse clickhouse-client \
--user clickhouse --password q1w2e3r4 \
--query "SELECT trace_id, span_id, name, type FROM default.events_full FORMAT PrettyCompact"
实测回显:
┌─trace_id─────────────────────────┬─span_id──────────┬─name──────────┬─type───────┐
1. │ 14a87e396596ce929e71fe5418ba6239 │ 9ecab8c7ce7cfbc3 │ demo-llm-call │ GENERATION │
└──────────────────────────────────┴──────────────────┴───────────────┴────────────┘
到这一步:Web 控制台 Traces 页面已能看到 demo-llm-call 这条记录,部署全链路(摄取 → 队列 → 对象存储 → ClickHouse → 查询展示)验证完毕。
五、数据持久化说明
v4 全栈数据分布在 3 个持久化引擎 + 1 个对象存储,compose 已用命名卷落盘(docker volume ls 可见 langfuse-4680-stack_langfuse_* 5 个卷):
| 卷名 | 容器内路径 | 存什么 |
|---|---|---|
| langfuse_postgres_data | /var/lib/postgresql/data | 用户/组织/项目/API Key/Prompt/数据集/审计日志 |
| langfuse_clickhouse_data | /var/lib/clickhouse | traces/observations/scores 等追踪宽表(default 库共 18 张表) |
| langfuse_clickhouse_logs | /var/log/clickhouse-server | ClickHouse 服务日志 |
| langfuse_minio_data | /data | 原始事件(events/)与媒体(media/)对象 |
| langfuse_redis_data | /data | BullMQ 队列与缓存(持久化可选,丢失仅影响未处理任务) |
MinIO 内事件对象按 events/otel/<项目ID>/<年>/<月>/<日>/<时>/ 分区存放,实测布局:
/data/langfuse:
events
/data/langfuse/events:
otel
/data/langfuse/events/otel/demo-project:
2026
/data/langfuse/events/otel/demo-project/2026:
10
验证持久化:docker compose restart 后登录态与已摄取的 trace 均在;docker compose down(不带 -v)同样保留数据;只有 docker compose down -v 会连卷一起删除(危险操作,见 4.3 排障实录中使用场景)。
六、环境变量表
全栈必改/常用变量速查(完整逐项详解见《Langfuse 4.68.0 配置参数详解》):
| 变量 | 作用域 | 本文取值 | 说明 |
|---|---|---|---|
| DATABASE_URL | web+worker | postgresql://postgres:q1w2e3r4@postgres:5432/postgres | PostgreSQL 连接串,口令必须替换 |
| NEXTAUTH_URL | web+worker | http://localhost:8146 | 控制台对外地址,登录回调依赖它 |
| NEXTAUTH_SECRET | web | k7P2mQx9vB4nR6tZ8wJcL5hF3dG1sA0eY | 会话签名密钥,openssl rand -base64 32 生成 |
| SALT | web+worker | vGmKxPYsJnWqRtZuHfE3bL8cA5dO9iU2 | API Key 哈希盐,建库后不可更换 |
| ENCRYPTION_KEY | web+worker | 5f8a2c6e...7a9f(64 位 hex) | 敏感字段加密密钥,openssl rand -hex 32 生成,建库后不可更换 |
| CLICKHOUSE_PASSWORD | web+worker+clickhouse | q1w2e3r4 | 三处保持一致 |
| REDIS_AUTH | web+worker+redis | q1w2e3r4redis | redis --requirepass 与客户端保持一致 |
| MINIO_ROOT_PASSWORD | minio | q1w2e3r4minio | 与 LANGFUSE_S3_*_SECRET_ACCESS_KEY 保持一致 |
| POSTGRES_PASSWORD | postgres | q1w2e3r4 | 与 DATABASE_URL 口令一致 |
| TELEMETRY_ENABLED | web+worker | false | 关闭匿名遥测上报 |
| LANGFUSE_INIT_ORG_ID / PROJECT_ID | web | demo-org / demo-project | 引导初始化的必填锚点 |
| LANGFUSE_INIT_USER_EMAIL / PASSWORD | web | root@langfuse.local / q1w2e3r4 | 引导管理员账号(生产必须更换) |
| LANGFUSE_INIT_PROJECT_PUBLIC_KEY / SECRET_KEY | web | pk-lf-4ff4ca96909a5c6a1316 / sk-lf-be93f07fe1be549f7b53693cee10e079 | 引导项目 API Key,SDK/OTLP 摄取用 |
| PORT / HOSTNAME | web | 3000 / 0.0.0.0 | 容器内监听,一般无需修改 |
⚠️ 注意:上表所有口令均为演示实例实测值,复制本指南时请全部替换为自行生成的随机值;SALT/ENCRYPTION_KEY 一旦产生生产数据即不可更换。
七、配置参数说明
v4 六服务拓扑中没有传统意义上的「配置文件」,全部配置经环境变量注入(12-factor 风格),因此:
- 改配置 = 改 compose 的
environment:段 →docker compose up -d重建对应服务生效; - ClickHouse/PostgreSQL/Redis 均使用容器默认配置 + compose 环境变量,未额外挂载配置文件;如需调优 ClickHouse,可挂载
users.xml/config.xml到/etc/clickhouse-server/(进阶场景,本指南不展开); - 官方镜像内的配置类文件只有迁移脚本与启动脚本(容器内路径
/app/packages/shared/scripts/cleanup.sql、/app/web/entrypoint.sh等),不需要也不建议手工修改。
八、等价 docker run 部署(Compose 不可用时的手工等价形态)
与上述 compose 完全等价的 6 条 docker run(共用同一自定义网络,逐条执行):
# 0. 创建共用网络(一次性)
docker network create --driver bridge --subnet 10.89.68.0/24 langfuse-4680-net
# 1. PostgreSQL
docker run -d --name langfuse-postgres --network langfuse-4680-net \
--restart always -p 127.0.0.1:25432:5432 \
-e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=q1w2e3r4 -e POSTGRES_DB=postgres \
-e TZ=UTC -e PGTZ=UTC \
-v langfuse_postgres_data:/var/lib/postgresql/data \
docker.io/postgres:17
# 2. ClickHouse
docker run -d --name langfuse-clickhouse --network langfuse-4680-net \
--restart always --user 101:101 -p 127.0.0.1:18123:8123 \
-e CLICKHOUSE_DB=default -e CLICKHOUSE_USER=clickhouse -e CLICKHOUSE_PASSWORD=q1w2e3r4 \
-v langfuse_clickhouse_data:/var/lib/clickhouse \
-v langfuse_clickhouse_logs:/var/log/clickhouse-server \
docker.io/clickhouse/clickhouse-server:25.12
# 3. MinIO(启动前先建 langfuse 桶目录)
docker run -d --name langfuse-minio --network langfuse-4680-net \
--restart always --entrypoint sh -p 127.0.0.1:19090:9000 -p 127.0.0.1:19091:9001 \
-e MINIO_ROOT_USER=minio -e MINIO_ROOT_PASSWORD=q1w2e3r4minio \
-v langfuse_minio_data:/data \
cgr.dev/chainguard/minio \
-c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
# 4. Redis(noeviction 为官方要求,队列不可淘汰)
docker run -d --name langfuse-redis --network langfuse-4680-net \
--restart always -p 127.0.0.1:26379:6379 \
-v langfuse_redis_data:/data \
docker.io/redis:7 --requirepass q1w2e3r4redis --maxmemory-policy noeviction
# 5. langfuse-worker(环境变量与 compose 的 worker 段一致,此处列关键项)
docker run -d --name langfuse-worker --network langfuse-4680-net \
--restart always -p 127.0.0.1:3030:3030 \
-e NEXTAUTH_URL=http://localhost:8146 \
-e DATABASE_URL=postgresql://postgres:q1w2e3r4@postgres:5432/postgres \
-e SALT=vGmKxPYsJnWqRtZuHfE3bL8cA5dO9iU2 \
-e ENCRYPTION_KEY=5f8a2c6e9b1d4f7a3c8e0b5d2a7f4c9e6b1d8a3f5c7e0b2d4a6f8c1e3b5d7a9f \
-e CLICKHOUSE_URL=http://clickhouse:8123 -e CLICKHOUSE_USER=clickhouse -e CLICKHOUSE_PASSWORD=q1w2e3r4 \
-e CLICKHOUSE_MIGRATION_URL=clickhouse://clickhouse:9000 \
-e REDIS_HOST=redis -e REDIS_AUTH=q1w2e3r4redis \
-e TELEMETRY_ENABLED=false \
docker.langfuse.com/langfuse/langfuse-worker:4.68.0
# 6. langfuse-web(worker 全部变量之上追加 NEXTAUTH_SECRET 与 INIT_*)
docker run -d --name langfuse-web --network langfuse-4680-net \
--restart always -p 8146:3000 \
-e NEXTAUTH_URL=http://localhost:8146 \
-e DATABASE_URL=postgresql://postgres:q1w2e3r4@postgres:5432/postgres \
-e SALT=vGmKxPYsJnWqRtZuHfE3bL8cA5dO9iU2 \
-e ENCRYPTION_KEY=5f8a2c6e9b1d4f7a3c8e0b5d2a7f4c9e6b1d8a3f5c7e0b2d4a6f8c1e3b5d7a9f \
-e NEXTAUTH_SECRET=k7P2mQx9vB4nR6tZ8wJcL5hF3dG1sA0eY \
-e CLICKHOUSE_URL=http://clickhouse:8123 -e CLICKHOUSE_USER=clickhouse -e CLICKHOUSE_PASSWORD=q1w2e3r4 \
-e CLICKHOUSE_MIGRATION_URL=clickhouse://clickhouse:9000 \
-e REDIS_HOST=redis -e REDIS_AUTH=q1w2e3r4redis \
-e TELEMETRY_ENABLED=false \
-e LANGFUSE_INIT_ORG_ID=demo-org -e LANGFUSE_INIT_PROJECT_ID=demo-project \
-e LANGFUSE_INIT_USER_EMAIL=root@langfuse.local \
-e LANGFUSE_INIT_USER_PASSWORD=q1w2e3r4 \
docker.langfuse.com/langfuse/langfuse:4.68.0
⚠️ 注意:手工形态需要自行控制启动顺序(先 1-4,健康后 5-6);实际运维强烈建议用 compose 的
depends_on: service_healthy代替人工时序。
九、常用运维命令
# 服务状态一览
docker compose ps
# 跟踪 Web 日志(迁移/告警都在这里)
docker compose logs -f langfuse-web
# 滚动重启单个服务(改环境变量后)
docker compose up -d langfuse-web
# 升级版本:改镜像标签(如 4.68.0 → 新版本)后重新拉起,迁移自动执行
docker compose pull && docker compose up -d
# 临时停栈(保留数据卷)
docker compose down
# 彻底清库重装(删除全部 5 个数据卷,不可恢复)
docker compose down -v
实测资源占用(docker stats --no-stream,空载稳态):
NAME MEM USAGE / LIMIT CPU %
langfuse-4680-stack-langfuse-web-1 674MiB / 31.3GiB 0.78%
langfuse-4680-stack-langfuse-worker-1 612.6MiB / 31.3GiB 0.38%
langfuse-4680-stack-postgres-1 79.03MiB / 31.3GiB 3.86%
langfuse-4680-stack-redis-1 6.086MiB / 31.3GiB 0.24%
langfuse-4680-stack-clickhouse-1 352.6MiB / 31.3GiB 5.32%
langfuse-4680-stack-minio-1 71.84MiB / 31.3GiB 3.75%
空载合计约 1.8GiB,8GB 内存服务器可稳定运行并留有摄取余量;ClickHouse 内存随查询并发增长,生产建议为其预留 2GB 以上。
十、常见问题
Q1:启动报 Bind for 127.0.0.1:15432 failed: port is already allocated 怎么办?
宿主机上该端口已被其他服务占用(本机实测就撞上了 15432)。定位:ss -ltn | grep <端口>;解决:换一个空闲端口改 compose 里的映射即可,容器间互访走 compose 网络内的服务名(postgres:5432),不受宿主端口影响。同理 5432/6379/8123/9090 这类高频默认端口都要先实查。
Q2:管理员账号没有被自动创建?
见 4.3 排障实录:LANGFUSE_INIT_* 必须同时含 LANGFUSE_INIT_ORG_ID 与 LANGFUSE_INIT_PROJECT_ID,缺了会被整体忽略并输出 warn。且 INIT 只在空库首次启动时生效。
Q3:POST /api/public/ingestion 返回 400 "Event type not accepted"?
v4 默认 LANGFUSE_MIGRATION_V4_WRITE_MODE=events_only,该端点只接受 score 事件;trace/observation 一律走 OpenTelemetry(POST /api/public/otel/v1/traces)或升级到发 OTLP 的新版官方 SDK。实测报错全文(节选):
{"successes":[],"errors":[{"id":"demo-trace-0001","status":400,"message":"Event type not accepted","error":"Event type \"trace-create\" is not accepted by /api/public/ingestion when LANGFUSE_MIGRATION_V4_WRITE_MODE is events_only. This endpoint only accepts score events. Upgrade to the latest Langfuse SDKs, which send traces via OpenTelemetry, or send traces directly to POST /api/public/otel/v1/traces. Scores can still be sent via POST /api/public/scores...
Q4:控制台能开但登录回调失败/跳转错地址?
NEXTAUTH_URL 与实际访问地址不一致(改过端口、加过反代)。改 compose 后 docker compose up -d langfuse-web 重建即可。
Q5:迁移失败如何排查?
按官方 entrypoint 的提示顺序查:PostgreSQL/ClickHouse/Redis/MinIO 是否健康(docker compose ps 看 healthy)、口令是否与 compose 一致、DATABASE_URL/CLICKHOUSE_PASSWORD 是否含未转义特殊字符(@ : / % # ? 等需百分号编码,这是官方启动脚本内置的检查项,失败时会输出 HINT 提示)。
十一、参考链接
- 官方 Compose 部署文档:https://langfuse.com/docs/deployment/docker-compose
- 自托管总览(含 K8s Helm / AWS Terraform 路径):https://langfuse.com/docs/deployment/self-hosting
- 官方 docker-compose.yml(main 分支,2026-10-23 检索):https://raw.githubusercontent.com/langfuse/langfuse/main/docker-compose.yml
- Langfuse Releases(版本核对):https://github.com/langfuse/langfuse/releases
- 本专栏系列:《Langfuse 介绍与选型指南》、《Langfuse 4.68.0 配置参数详解》、《Langfuse 4.68.0 常用 CLI 命令与工具详解》