Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions blueprints/langfuse/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Langfuse self-hosted, adapted from the official docker-compose.yml:
# https://github.com/langfuse/langfuse/blob/main/docker-compose.yml
#
# MinIO is exposed on its own domain because Langfuse's web UI generates
# presigned S3 URLs that the browser fetches directly (e.g. media in
# traces) - it cannot resolve the internal "minio" hostname.

services:
postgres:
image: docker.io/postgres:17
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: postgres
TZ: UTC
PGTZ: UTC
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 3s
timeout: 3s
retries: 10
volumes:
- langfuse-postgres-data:/var/lib/postgresql/data

clickhouse:
image: docker.io/clickhouse/clickhouse-server:25.12
restart: unless-stopped
user: "101:101"
environment:
CLICKHOUSE_DB: default
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD}
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1
interval: 5s
timeout: 5s
retries: 10
start_period: 1s
volumes:
- langfuse-clickhouse-data:/var/lib/clickhouse
- langfuse-clickhouse-logs:/var/log/clickhouse-server

minio:
image: cgr.dev/chainguard/minio
restart: unless-stopped
entrypoint: sh
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 1s
timeout: 5s
retries: 5
start_period: 1s
volumes:
- langfuse-minio-data:/data

redis:
image: docker.io/redis:7
restart: unless-stopped
command: >
--requirepass ${REDIS_AUTH}
--maxmemory-policy noeviction
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 10s
retries: 10
volumes:
- langfuse-redis-data:/data

langfuse-worker:
image: docker.io/langfuse/langfuse-worker:4
restart: unless-stopped
depends_on: &langfuse-depends-on
postgres:
condition: service_healthy
minio:
condition: service_healthy
redis:
condition: service_healthy
clickhouse:
condition: service_healthy
environment: &langfuse-worker-env
NEXTAUTH_URL: ${NEXTAUTH_URL}
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
SALT: ${SALT}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
TELEMETRY_ENABLED: "true"
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: "false"
CLICKHOUSE_MIGRATION_URL: clickhouse://clickhouse:9000
CLICKHOUSE_URL: http://clickhouse:8123
CLICKHOUSE_USER: clickhouse
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD}
CLICKHOUSE_CLUSTER_ENABLED: "false"
LANGFUSE_S3_EVENT_UPLOAD_BUCKET: langfuse
LANGFUSE_S3_EVENT_UPLOAD_REGION: auto
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: minio
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD}
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: http://minio:9000
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true"
LANGFUSE_S3_EVENT_UPLOAD_PREFIX: events/
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: langfuse
LANGFUSE_S3_MEDIA_UPLOAD_REGION: auto
LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: minio
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD}
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: http://minio:9000
LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: "true"
LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: media/
LANGFUSE_S3_BATCH_EXPORT_ENABLED: "false"
REDIS_HOST: redis
REDIS_PORT: "6379"
REDIS_AUTH: ${REDIS_AUTH}
REDIS_TLS_ENABLED: "false"

langfuse-web:
image: docker.io/langfuse/langfuse:4
restart: unless-stopped
depends_on: *langfuse-depends-on
environment:
<<: *langfuse-worker-env
# Bind Next.js to all interfaces: Docker sets HOSTNAME to the container id,
# which resolves to a single network's IP - with Dokploy's extra proxy
# network attached, Traefik would get connection refused (502) otherwise.
HOSTNAME: "0.0.0.0"
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
# Overrides the worker's internal endpoint above: the browser resolves
# presigned media URLs itself, so this one must be publicly reachable.
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT}

volumes:
langfuse-postgres-data:
langfuse-clickhouse-data:
langfuse-clickhouse-logs:
langfuse-minio-data:
langfuse-redis-data:
26 changes: 26 additions & 0 deletions blueprints/langfuse/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Langfuse

Self-hosted Langfuse: open-source LLM tracing, prompt management, evaluation and cost/usage analytics, based on the official `docker-compose.yml`.

## Two domains

This template exposes two domains:

- The **main domain** serves the Langfuse web app/API (`langfuse-web`, port 3000).
- The **second domain** exposes MinIO's S3 API (port 9000) directly. Langfuse's web UI generates presigned URLs for media (e.g. images attached to traces) that the *browser* fetches straight from MinIO, so this endpoint has to be publicly reachable — it isn't just an internal implementation detail.

## First boot

`langfuse-web` and `langfuse-worker` wait on Postgres, ClickHouse, Redis and MinIO to report healthy, then run their own migrations on startup. Once `langfuse-web` is up, open its domain and create the first user — they become the owner of the initial organization.

## After enabling HTTPS

By default `NEXTAUTH_URL` and `LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT` are set to `http://`. If you enable HTTPS on either domain (recommended for production), update the matching environment variable to `https://<domain>` and redeploy, otherwise auth callbacks and media URLs will point at the wrong scheme.

## Licensing

The core of Langfuse (tracing, prompt management, evaluation, playground) is open-source (MIT) and fully usable self-hosted without a license key. Some enterprise-only features (e.g. SSO/SAML, fine-grained RBAC) live under a separate commercial license — see [self-hosting license docs](https://langfuse.com/self-hosting/license-key). This template does not configure a license key.

## Versioning

Images are pinned to the `4` major tag (`langfuse/langfuse:4`, `langfuse/langfuse-worker:4`), matching the upstream compose file. To pin to a specific release instead, replace `4` with a version tag from [Docker Hub](https://hub.docker.com/r/langfuse/langfuse/tags).
8 changes: 8 additions & 0 deletions blueprints/langfuse/langfuse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions blueprints/langfuse/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": "langfuse",
"name": "Langfuse",
"version": "4",
"description": "Langfuse is an open-source LLM engineering platform for tracing, prompt management, evaluation, and cost/usage analytics of LLM applications.",
"logo": "langfuse.svg",
"links": {
"github": "https://github.com/langfuse/langfuse",
"website": "https://langfuse.com/",
"docs": "https://langfuse.com/self-hosting"
},
"tags": ["ai", "llm", "observability", "analytics", "monitoring"]
}
36 changes: 36 additions & 0 deletions blueprints/langfuse/template.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[variables]
main_domain = "${domain}"
minio_domain = "${domain}"
postgres_password = "${password:32}"
clickhouse_password = "${password:32}"
minio_root_password = "${password:32}"
redis_auth = "${password:32}"
nextauth_secret = "${password:32}"
salt = "${password:32}"
encryption_key = "${hash:64}"
nextauth_url = "http://${main_domain}"
minio_public_endpoint = "http://${minio_domain}"

[config]
[[config.domains]]
serviceName = "langfuse-web"
port = 3000
host = "${main_domain}"

[[config.domains]]
serviceName = "minio"
port = 9000
host = "${minio_domain}"

[config.env]
POSTGRES_PASSWORD = "${postgres_password}"
CLICKHOUSE_PASSWORD = "${clickhouse_password}"
MINIO_ROOT_PASSWORD = "${minio_root_password}"
REDIS_AUTH = "${redis_auth}"
NEXTAUTH_SECRET = "${nextauth_secret}"
SALT = "${salt}"
ENCRYPTION_KEY = "${encryption_key}"
# Set to https://<main-domain> once HTTPS is enabled on the domain
NEXTAUTH_URL = "${nextauth_url}"
# Set to https://<minio-domain> once HTTPS is enabled on the domain
LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT = "${minio_public_endpoint}"
Loading