How to Deploy Langfuse - Open-Source LLM Observability Platform

Langfuse is an open-source observability platform for applications powered by Large Language Models (LLMs). It traces prompts and responses, monitors token usage and costs, and provides analytics for debugging AI workflows in production. Langfuse helps developers optimize LLM application performance and control operational expenses.
This article explains how to deploy Langfuse on a Linux server using Docker Compose with Object Storage for trace data persistence and a Traefik reverse proxy that provisions an HTTPS certificate through Let's Encrypt.
Prerequisites
Before you begin, you need to:
- Have access to a Linux-based server (with at least 4 CPU cores and 16 GB of RAM) as a non-root user with sudo privileges.
- Install Docker and Docker Compose.
- Create an Object Storage subscription and a bucket for storing Langfuse artifacts.
- Create a DNS A record pointing to your server's IP address (for example,
langfuse.example.com).
Set Up the Directory Structure and Environment Variables
Langfuse requires environment variables for domain configuration, database credentials, encryption keys, and Object Storage access.
Create the project directory with subdirectories for persistent data.
console$ mkdir -p ~/langfuse/{letsencrypt,postgres,clickhouse-data,clickhouse-logs,redis}
letsencrypt: Stores SSL/TLS certificates.postgres: Persists PostgreSQL database files.clickhouse-data: Persists ClickHouse analytics data.clickhouse-logs: Stores ClickHouse server logs.redis: Persists Redis data.
Navigate to the project directory.
console$ cd ~/langfuse
ClickHouse runs as UID 101 inside the container. Set matching ownership on its host directories so the container can write to them.
console$ sudo chown -R 101:101 clickhouse-data clickhouse-logs
Generate secure secrets for the application. Run this command six times and save each output for use in the environment file.
console$ openssl rand -hex 32
The first three values are for
SALT,ENCRYPTION_KEY, andNEXTAUTH_SECRET. The remaining three are forPOSTGRES_PASSWORD,CLICKHOUSE_PASSWORD, andREDIS_AUTH.Create an
.envfile to store configuration values.console$ nano .env
Add the following environment variables. Replace
langfuse.example.comwith your domain,admin@example.comwith your email, theGENERATED_SECRET_*placeholders with the six secrets you generated, and the Object Storage placeholders with your bucket credentials. InDATABASE_URL, use the same value asPOSTGRES_PASSWORD.ini# Domain and HTTPS Configuration DOMAIN=langfuse.example.com LETSENCRYPT_EMAIL=admin@example.com NEXTAUTH_URL=https://langfuse.example.com # Langfuse Secrets SALT=GENERATED_SECRET_1 ENCRYPTION_KEY=GENERATED_SECRET_2 NEXTAUTH_SECRET=GENERATED_SECRET_3 # PostgreSQL Configuration POSTGRES_USER=langfuse POSTGRES_PASSWORD=GENERATED_SECRET_4 POSTGRES_DB=langfuse DATABASE_URL=postgresql://langfuse:GENERATED_SECRET_4@postgres:5432/langfuse # ClickHouse Configuration CLICKHOUSE_USER=langfuse CLICKHOUSE_PASSWORD=GENERATED_SECRET_5 CLICKHOUSE_MIGRATION_URL=clickhouse://clickhouse:9000 CLICKHOUSE_URL=http://clickhouse:8123 # Redis Configuration REDIS_HOST=redis REDIS_PORT=6379 REDIS_AUTH=GENERATED_SECRET_6 # Object Storage Configuration S3_BUCKET=YOUR_BUCKET_NAME S3_REGION=YOUR_REGION S3_ENDPOINT=https://YOUR_S3_ENDPOINT S3_ACCESS_KEY=YOUR_ACCESS_KEY S3_SECRET_KEY=YOUR_SECRET_KEY # Application Configuration TELEMETRY_ENABLED=true LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=false
TheNoteTELEMETRY_ENABLED=truesetting allows Langfuse to send anonymous usage statistics to help improve Langfuse. No sensitive data, prompts, or trace content is transmitted. Set this tofalseif your organization has strict data retention policies or compliance requirements.Save and close the file.
Deploy Langfuse with Docker Compose
The deployment stack runs six containerized services that work together to provide the complete Langfuse observability platform. Traefik handles HTTPS and automatic certificate provisioning through Let's Encrypt, while PostgreSQL, ClickHouse, and Redis provide data persistence, analytics, and caching. Langfuse consists of two services: the web application and a background worker for asynchronous processing. Object Storage handles trace data, media uploads, and batch exports. This configuration is based on the official Langfuse Docker Compose examples.
Create the Docker Compose manifest file.
console$ nano docker-compose.yml
Add the following contents:
yamlservices: traefik: image: traefik:v3.7.0 container_name: traefik restart: unless-stopped environment: DOCKER_API_VERSION: "1.44" command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--certificatesresolvers.le.acme.httpchallenge=true" - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" - "--certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}" - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt postgres: image: postgres:17 container_name: langfuse-postgres restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} TZ: UTC PGTZ: UTC ports: - "127.0.0.1:5432:5432" volumes: - ./postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 3s timeout: 3s retries: 10 clickhouse: image: clickhouse/clickhouse-server:26.5.1-alpine container_name: langfuse-clickhouse restart: unless-stopped user: "101:101" environment: CLICKHOUSE_DB: default CLICKHOUSE_USER: ${CLICKHOUSE_USER} CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} ports: - "127.0.0.1:8123:8123" - "127.0.0.1:9000:9000" volumes: - ./clickhouse-data:/var/lib/clickhouse - ./clickhouse-logs:/var/log/clickhouse-server healthcheck: test: wget --no-verbose --tries=1 --spider http://127.0.0.1:8123/ping || exit 1 interval: 5s timeout: 5s retries: 10 start_period: 1s redis: image: redis:7-alpine container_name: langfuse-redis restart: unless-stopped command: > --requirepass ${REDIS_AUTH} --maxmemory-policy noeviction ports: - "127.0.0.1:6379:6379" volumes: - ./redis:/data healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 3s timeout: 10s retries: 10 langfuse-worker: image: langfuse/langfuse-worker:3 container_name: langfuse-worker restart: unless-stopped depends_on: postgres: condition: service_healthy redis: condition: service_healthy clickhouse: condition: service_healthy ports: - "127.0.0.1:3030:3030" environment: DATABASE_URL: ${DATABASE_URL} NEXTAUTH_URL: ${NEXTAUTH_URL} SALT: ${SALT} ENCRYPTION_KEY: ${ENCRYPTION_KEY} TELEMETRY_ENABLED: ${TELEMETRY_ENABLED} LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES} CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL} CLICKHOUSE_URL: ${CLICKHOUSE_URL} CLICKHOUSE_USER: ${CLICKHOUSE_USER} CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} CLICKHOUSE_CLUSTER_ENABLED: "false" REDIS_HOST: ${REDIS_HOST} REDIS_PORT: ${REDIS_PORT} REDIS_AUTH: ${REDIS_AUTH} REDIS_TLS_ENABLED: "false" LANGFUSE_S3_EVENT_UPLOAD_BUCKET: ${S3_BUCKET} LANGFUSE_S3_EVENT_UPLOAD_REGION: ${S3_REGION} LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: ${S3_ACCESS_KEY} LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${S3_SECRET_KEY} LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true" LANGFUSE_S3_EVENT_UPLOAD_PREFIX: "events/" LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: ${S3_BUCKET} LANGFUSE_S3_MEDIA_UPLOAD_REGION: ${S3_REGION} LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: ${S3_ACCESS_KEY} LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${S3_SECRET_KEY} LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: "true" LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: "media/" LANGFUSE_S3_BATCH_EXPORT_ENABLED: "true" LANGFUSE_S3_BATCH_EXPORT_BUCKET: ${S3_BUCKET} LANGFUSE_S3_BATCH_EXPORT_REGION: ${S3_REGION} LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID: ${S3_ACCESS_KEY} LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY: ${S3_SECRET_KEY} LANGFUSE_S3_BATCH_EXPORT_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_BATCH_EXPORT_EXTERNAL_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE: "true" LANGFUSE_S3_BATCH_EXPORT_PREFIX: "exports/" langfuse-web: image: langfuse/langfuse:3 container_name: langfuse-web restart: unless-stopped depends_on: postgres: condition: service_healthy redis: condition: service_healthy clickhouse: condition: service_healthy ports: - "3000:3000" environment: DATABASE_URL: ${DATABASE_URL} NEXTAUTH_URL: ${NEXTAUTH_URL} NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} SALT: ${SALT} ENCRYPTION_KEY: ${ENCRYPTION_KEY} TELEMETRY_ENABLED: ${TELEMETRY_ENABLED} LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES} CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL} CLICKHOUSE_URL: ${CLICKHOUSE_URL} CLICKHOUSE_USER: ${CLICKHOUSE_USER} CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} CLICKHOUSE_CLUSTER_ENABLED: "false" REDIS_HOST: ${REDIS_HOST} REDIS_PORT: ${REDIS_PORT} REDIS_AUTH: ${REDIS_AUTH} REDIS_TLS_ENABLED: "false" LANGFUSE_S3_EVENT_UPLOAD_BUCKET: ${S3_BUCKET} LANGFUSE_S3_EVENT_UPLOAD_REGION: ${S3_REGION} LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: ${S3_ACCESS_KEY} LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${S3_SECRET_KEY} LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true" LANGFUSE_S3_EVENT_UPLOAD_PREFIX: "events/" LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: ${S3_BUCKET} LANGFUSE_S3_MEDIA_UPLOAD_REGION: ${S3_REGION} LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: ${S3_ACCESS_KEY} LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${S3_SECRET_KEY} LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: "true" LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: "media/" LANGFUSE_S3_BATCH_EXPORT_ENABLED: "true" LANGFUSE_S3_BATCH_EXPORT_BUCKET: ${S3_BUCKET} LANGFUSE_S3_BATCH_EXPORT_REGION: ${S3_REGION} LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID: ${S3_ACCESS_KEY} LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY: ${S3_SECRET_KEY} LANGFUSE_S3_BATCH_EXPORT_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_BATCH_EXPORT_EXTERNAL_ENDPOINT: ${S3_ENDPOINT} LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE: "true" LANGFUSE_S3_BATCH_EXPORT_PREFIX: "exports/" labels: - "traefik.enable=true" - "traefik.http.routers.langfuse.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.langfuse.entrypoints=websecure" - "traefik.http.routers.langfuse.tls=true" - "traefik.http.routers.langfuse.tls.certresolver=le" - "traefik.http.services.langfuse.loadbalancer.server.port=3000"
Save and close the file.
In the above manifest:
- services: Launches six containers managed by Docker Compose:
- traefik: Serves as the reverse proxy and TLS termination point with automatic Let's Encrypt certificate provisioning.
- postgres: Stores Langfuse metadata including users, projects, and trace information in a persistent database.
- clickhouse: Provides the analytics database for time-series trace data and metrics queries.
- redis: Handles caching and background job queue management.
- langfuse-worker: Processes trace data asynchronously and manages batch exports to Object Storage.
- langfuse-web: Runs the main web application and API interface.
- environment: Configures database credentials, encryption keys, and Object Storage settings. The
LANGFUSE_S3_*variables connect Langfuse to Object Storage for storing trace events, media uploads, and batch exports. - depends_on: Ensures Langfuse services start only after database containers become healthy.
- labels (langfuse-web): Registers the web service with Traefik for HTTPS routing on the configured domain.
- volumes: Named volumes persist data for PostgreSQL, ClickHouse, Redis, and Let's Encrypt certificates across container restarts.
- restart: unless-stopped: Enables automatic recovery after failures or server reboots.
- services: Launches six containers managed by Docker Compose:
Start the services.
console$ docker compose up -d
Verify all containers are running.
console$ docker compose ps -a
The output displays six running containers in an
Upstate, with a healthy status shown forpostgres,clickhouse, andredis, since those are the only services with a configured health check.View the service logs to confirm all components started successfully.
console$ docker compose logs
For more information on managing a Docker Compose stack, see the How To Use Docker Compose article.
Access and Configure Langfuse
Langfuse provides a web-based dashboard for managing projects, viewing traces, and generating API keys.
Open a web browser and navigate to
https://langfuse.example.com, replacinglangfuse.example.comwith your configured domain.
The Langfuse welcome page displays signup and login options.
On the Langfuse welcome page, click Sign up to create an administrator account.
Enter your email address and password, then click Sign up.
After signing in, create a new organization. Enter your organization name (for example,
My Company) and click Create Organization.Create a new project within the organization. Enter your project name (for example,
Production LLM) and click Create Project.Langfuse displays a setup wizard for your new project. Click Create API keys to generate authentication credentials.
Copy and securely store both the Secret Key (starting with
sk-lf-) and Public Key (starting withpk-lf-). The secret key is only displayed once and cannot be retrieved later.
Your Langfuse instance is now configured and ready to receive trace data from your applications.
Application Use Case
Langfuse tracks LLM interactions by logging prompts, responses, and metadata to the server. The following example demonstrates sending a test trace to verify the deployment.
Install the Python virtual environment package.
console$ sudo apt install python3-venv -y
Create a virtual environment.
console$ python3 -m venv langfuse-env
Activate the virtual environment.
console$ source langfuse-env/bin/activate
Install the required Python packages.
console$ pip install langfuse openai
Create a test script.
console$ nano test_langfuse.py
Add the following code. Replace
YOUR_LLM_API_KEYwith an API key from your LLM provider. This example uses Groq's API endpoint. For other providers, adjust thebase_urlparameter.pythonfrom langfuse.openai import openai # Initialize OpenAI-compatible client client = openai.OpenAI( api_key="YOUR_LLM_API_KEY", base_url="https://api.groq.com/openai/v1" ) # Send a chat completion request response = client.chat.completions.create( model="openai/gpt-oss-120b", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Explain Langfuse in one sentence."} ], temperature=0.7, max_tokens=100 ) print(response.choices[0].message.content)
Save and close the file.
Set environment variables for Langfuse authentication. Replace
YOUR_SECRET_KEY,YOUR_PUBLIC_KEY, andlangfuse.example.comwith your actual API keys and domain name.console$ export LANGFUSE_SECRET_KEY="YOUR_SECRET_KEY" $ export LANGFUSE_PUBLIC_KEY="YOUR_PUBLIC_KEY" $ export LANGFUSE_HOST="https://langfuse.example.com"
Run the test script.
console$ python test_langfuse.py
The script sends a request to the LLM provider, and the Langfuse SDK automatically captures and forwards the trace to your Langfuse instance.
Open the Langfuse web interface at your configured domain and navigate to the Traces page in your project.

The Traces page displays captured traces. Click the trace to view detailed information.

The trace details show model name, token usage, latency, cost estimate, and the full request/response conversation.
Conclusion
You have successfully deployed Langfuse on a Linux server using Docker Compose with Object Storage for trace data persistence and Traefik providing automatic HTTPS through Let's Encrypt. The deployment provides a fully functional LLM observability platform accessible securely over a custom domain with persistent storage for PostgreSQL, ClickHouse, and Redis data. You can now integrate Langfuse with your production LLM applications to monitor prompts, responses, token usage, and costs. For more information on advanced features and integrations, refer to the official Langfuse documentation.