diff --git a/blueprints/langfuse/docker-compose.yml b/blueprints/langfuse/docker-compose.yml new file mode 100644 index 000000000..81dcdf1fa --- /dev/null +++ b/blueprints/langfuse/docker-compose.yml @@ -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: diff --git a/blueprints/langfuse/instructions.md b/blueprints/langfuse/instructions.md new file mode 100644 index 000000000..5abefd188 --- /dev/null +++ b/blueprints/langfuse/instructions.md @@ -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://` 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). diff --git a/blueprints/langfuse/langfuse.svg b/blueprints/langfuse/langfuse.svg new file mode 100644 index 000000000..d56d582be --- /dev/null +++ b/blueprints/langfuse/langfuse.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/langfuse/meta.json b/blueprints/langfuse/meta.json new file mode 100644 index 000000000..7b39595eb --- /dev/null +++ b/blueprints/langfuse/meta.json @@ -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"] +} diff --git a/blueprints/langfuse/template.toml b/blueprints/langfuse/template.toml new file mode 100644 index 000000000..9d388a88b --- /dev/null +++ b/blueprints/langfuse/template.toml @@ -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:// once HTTPS is enabled on the domain +NEXTAUTH_URL = "${nextauth_url}" +# Set to https:// once HTTPS is enabled on the domain +LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT = "${minio_public_endpoint}"