Langfuse Docker Compose 部署指南

Langfuse v4.68.0 Docker Compose 六服务全栈部署实录:迁移验证、NextAuth 登录链路、OTLP 端到端摄取与 ClickHouse 落表核对,含端口冲突与 INIT 引导排障

LangfuseLangfuseDockerLLM2026/10/23

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-webdocker.langfuse.com/langfuse/langfuse:4.68.01.48GB官方 Web 镜像,内含 Next.js 应用与 prisma CLI,启动时自动执行数据库迁移
langfuse-workerdocker.langfuse.com/langfuse/langfuse-worker:4.68.01.88GB官方 Worker 镜像,队列消费与 ClickHouse 写入
postgresdocker.io/postgres:17646MBv4 官方 Compose 默认 POSTGRES_VERSION:-17
clickhousedocker.io/clickhouse/clickhouse-server:25.121.13GB官方 Compose 锁定 25.12;Langfuse 对 ClickHouse 版本有兼容性矩阵(启动日志会输出 computedCompatibilityFlags)
miniocgr.dev/chainguard/minio236MB官方 Compose 采用的 Chainguard 最小化镜像
redisdocker.io/redis:7170MB官方 Compose 锁定大版本 7

💡 说明:镜像合计约 5.5GB,磁盘预留 10GB 以上(含 ClickHouse 数据增长)。镜像标签 :4 会随官方更新浮动,生产建议写死 :4.68.0 这类精确版本,升级时显式改标签。

三、前置条件

  1. 服务器已安装 Docker Engine 29.x 与 compose 插件(docker compose version 可用);
  2. 磁盘剩余 ≥10GB,内存 ≥4GB(推荐 ≥8GB);
  3. 网络可拉取 docker.langfuse.com、docker.io、cgr.dev 三个镜像仓库(离线环境可先在能联网的机器 docker save/docker load 搬运镜像);
  4. 端口规划确认:目标宿主端口未被占用,逐个实查:
# 实查本机监听端口,确认 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,仅映射端口变化,不影响功能)。

  1. 生成部署所需的随机密钥(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/clickhousetraces/observations/scores 等追踪宽表(default 库共 18 张表)
langfuse_clickhouse_logs/var/log/clickhouse-serverClickHouse 服务日志
langfuse_minio_data/data原始事件(events/)与媒体(media/)对象
langfuse_redis_data/dataBullMQ 队列与缓存(持久化可选,丢失仅影响未处理任务)

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_URLweb+workerpostgresql://postgres:q1w2e3r4@postgres:5432/postgresPostgreSQL 连接串,口令必须替换
NEXTAUTH_URLweb+workerhttp://localhost:8146控制台对外地址,登录回调依赖它
NEXTAUTH_SECRETwebk7P2mQx9vB4nR6tZ8wJcL5hF3dG1sA0eY会话签名密钥,openssl rand -base64 32 生成
SALTweb+workervGmKxPYsJnWqRtZuHfE3bL8cA5dO9iU2API Key 哈希盐,建库后不可更换
ENCRYPTION_KEYweb+worker5f8a2c6e...7a9f(64 位 hex)敏感字段加密密钥,openssl rand -hex 32 生成,建库后不可更换
CLICKHOUSE_PASSWORDweb+worker+clickhouseq1w2e3r4三处保持一致
REDIS_AUTHweb+worker+redisq1w2e3r4redisredis --requirepass 与客户端保持一致
MINIO_ROOT_PASSWORDminioq1w2e3r4minio与 LANGFUSE_S3_*_SECRET_ACCESS_KEY 保持一致
POSTGRES_PASSWORDpostgresq1w2e3r4与 DATABASE_URL 口令一致
TELEMETRY_ENABLEDweb+workerfalse关闭匿名遥测上报
LANGFUSE_INIT_ORG_ID / PROJECT_IDwebdemo-org / demo-project引导初始化的必填锚点
LANGFUSE_INIT_USER_EMAIL / PASSWORDwebroot@langfuse.local / q1w2e3r4引导管理员账号(生产必须更换)
LANGFUSE_INIT_PROJECT_PUBLIC_KEY / SECRET_KEYwebpk-lf-4ff4ca96909a5c6a1316 / sk-lf-be93f07fe1be549f7b53693cee10e079引导项目 API Key,SDK/OTLP 摄取用
PORT / HOSTNAMEweb3000 / 0.0.0.0容器内监听,一般无需修改

⚠️ 注意:上表所有口令均为演示实例实测值,复制本指南时请全部替换为自行生成的随机值;SALT/ENCRYPTION_KEY 一旦产生生产数据即不可更换。

七、配置参数说明

v4 六服务拓扑中没有传统意义上的「配置文件」,全部配置经环境变量注入(12-factor 风格),因此:

  1. 改配置 = 改 compose 的 environment: 段 → docker compose up -d 重建对应服务生效;
  2. ClickHouse/PostgreSQL/Redis 均使用容器默认配置 + compose 环境变量,未额外挂载配置文件;如需调优 ClickHouse,可挂载 users.xml/config.xml 到 /etc/clickhouse-server/(进阶场景,本指南不展开);
  3. 官方镜像内的配置类文件只有迁移脚本与启动脚本(容器内路径 /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 提示)。

十一、参考链接

  1. 官方 Compose 部署文档:https://langfuse.com/docs/deployment/docker-compose
  2. 自托管总览(含 K8s Helm / AWS Terraform 路径):https://langfuse.com/docs/deployment/self-hosting
  3. 官方 docker-compose.yml(main 分支,2026-10-23 检索):https://raw.githubusercontent.com/langfuse/langfuse/main/docker-compose.yml
  4. Langfuse Releases(版本核对):https://github.com/langfuse/langfuse/releases
  5. 本专栏系列:《Langfuse 介绍与选型指南》、《Langfuse 4.68.0 配置参数详解》、《Langfuse 4.68.0 常用 CLI 命令与工具详解》