Every prompt, every completion, every token count β when youβre building AI applications, observability isnβt optional. Langfuse is one of the best open-source platforms for tracing and monitoring LLM apps, and the self-hosted option means your data never leaves your infrastructure. This guide walks you through a complete Docker Compose deployment with PostgreSQL and ClickHouse so you can have production-grade AI observability running in minutes.
If youβre new to Langfuse, start with our complete Langfuse guide for an overview of its features and how it fits into the broader LLM observability landscape.
Why Self-Host Langfuse?
The managed Langfuse Cloud works well, but self-hosting makes sense in several scenarios:
- GDPR and data privacy β Prompts and completions often contain personal data. Keeping everything on-prem removes third-party data processing concerns entirely. We cover this in depth in our self-hosted AI and GDPR guide.
- Data sovereignty β Regulated industries (healthcare, finance, government) often require that data stays within specific jurisdictions or networks.
- Air-gapped environments β If your infrastructure has no outbound internet access, self-hosting is the only path. Pair it with a local Ollama setup for a fully offline AI stack.
- Cost control β High-volume tracing can get expensive on managed plans. Self-hosting costs you only the compute and storage you provision.
- Customization β Full control over retention policies, backup schedules, and network configuration.
For a broader look at why enterprises are moving toward self-hosted AI tooling, see our enterprise self-hosted AI guide.
Prerequisites
Before starting, make sure you have:
- Docker Engine 24+ and Docker Compose v2 installed
- At least 4 GB of free RAM (8 GB recommended for production workloads)
- 2 CPU cores minimum
- 10 GB disk space for the database and ClickHouse storage
- A machine running Linux, macOS, or Windows with WSL2
Docker Compose Setup
Create a project directory and add the following docker-compose.yml:
mkdir langfuse-self-hosted && cd langfuse-self-hosted
# docker-compose.yml
version: "3.9"
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: langfuse
POSTGRES_USER: langfuse
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-langfuse-secret}
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U langfuse"]
interval: 5s
timeout: 3s
retries: 5
clickhouse:
image: clickhouse/clickhouse-server:24-alpine
restart: unless-stopped
environment:
CLICKHOUSE_DB: langfuse
CLICKHOUSE_USER: langfuse
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse-secret}
volumes:
- ch_data:/var/lib/clickhouse
healthcheck:
test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
interval: 5s
timeout: 3s
retries: 5
langfuse:
image: langfuse/langfuse:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
clickhouse:
condition: service_healthy
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://langfuse:${POSTGRES_PASSWORD:-langfuse-secret}@postgres:5432/langfuse
CLICKHOUSE_URL: http://clickhouse:8123
CLICKHOUSE_USER: langfuse
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse-secret}
NEXTAUTH_URL: http://localhost:3000
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-change-me-to-a-random-string}
SALT: ${SALT:-another-random-string-here}
TELEMETRY_ENABLED: "false"
volumes:
pg_data:
ch_data:
Environment Variables
Create a .env file next to your compose file with secure values:
# .env
POSTGRES_PASSWORD=your-strong-pg-password
CLICKHOUSE_PASSWORD=your-strong-ch-password
NEXTAUTH_SECRET=$(openssl rand -base64 32)
SALT=$(openssl rand -base64 32)
Key variables explained:
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection string for Langfuse metadata |
CLICKHOUSE_URL | ClickHouse endpoint for high-volume trace storage |
NEXTAUTH_SECRET | Signs session tokens β must be random and kept secret |
SALT | Used for hashing API keys β generate once, never change |
TELEMETRY_ENABLED | Set to false to disable anonymous usage reporting |
Starting Langfuse
Bring everything up:
docker compose up -d
Watch the logs until Langfuse reports itβs ready:
docker compose logs -f langfuse
You should see output indicating the database migrations completed and the server is listening on port 3000. Open http://localhost:3000 in your browser.
First Login
- Navigate to
http://localhost:3000 - Click Sign Up to create your admin account
- After signing in, go to Settings β API Keys
- Create a new API key pair β youβll need the Public Key and Secret Key for your application
Save these keys securely. Youβll use them in the next step.
Connecting Your Python App
Install the Langfuse Python SDK:
pip install langfuse
Add tracing to your application with the decorator approach:
from langfuse.decorators import observe, langfuse_context
import os
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "http://localhost:3000"
@observe()
def generate_response(user_input: str) -> str:
# Your LLM call here (OpenAI, Anthropic, local model, etc.)
response = call_your_llm(user_input)
return response
result = generate_response("Explain quantum computing")
langfuse_context.flush()
The @observe() decorator automatically captures inputs, outputs, latency, and token usage. Call flush() at the end to ensure all traces are sent before the process exits.
Verifying Traces
After running your instrumented code:
- Open the Langfuse dashboard at
http://localhost:3000 - Navigate to Traces in the sidebar
- You should see your function call with full input/output details, timing, and metadata
If traces donβt appear, check that:
- Your
LANGFUSE_HOSTpoints to the correct URL - The API keys match whatβs configured in the dashboard
- You called
flush()before the process exited - The Langfuse container is healthy:
docker compose ps
Backup Strategy
Both databases need regular backups:
# PostgreSQL backup
docker compose exec postgres pg_dump -U langfuse langfuse > backup_pg_$(date +%Y%m%d).sql
# ClickHouse backup
docker compose exec clickhouse clickhouse-client \
--user langfuse \
--password your-strong-ch-password \
--query "BACKUP DATABASE langfuse TO Disk('backups', 'langfuse_$(date +%Y%m%d)')"
Schedule these with cron for automated daily backups. Store copies off-host β a mounted NFS share or an object storage bucket works well.
Updating Langfuse
To pull the latest version:
docker compose pull langfuse
docker compose up -d langfuse
Langfuse runs database migrations automatically on startup. Check the Langfuse changelog before upgrading to review breaking changes. For major version bumps, snapshot your databases first.
Whatβs Next
You now have a fully self-hosted AI observability stack. From here you can:
- Add more team members and configure role-based access
- Set up prompt management and versioning through the Langfuse UI
- Create evaluation datasets for systematic testing
- Configure alerting on latency or error rate thresholds
- Put Langfuse behind a reverse proxy with TLS for production use
Pair this setup with a self-hosted Ollama instance and you have a completely private AI development environment β no data leaves your network, ever.