Un agente que trabaja a las tres de la madrugada sin nadie mirando vale lo que vale tu capacidad de reconstruir después qué hizo. «Quemó 38 $ en tokens durante la noche» es una mala mañana. «Quemó 38 $ porque una herramienta de búsqueda devolvió una página vacía y el planificador lo reintentó 400 veces» es un bug que arreglas antes de comer. Para la segunda frase hacen falta trazas.
Langfuse registra cada llamada al LLM, cada prompt, paso de herramienta, recuento de tokens y latencia como un árbol que puedes recorrer con clics. Si lo alojas tú, esos prompts y respuestas —que suelen contener datos de clientes, salidas de API y algún secreto despistado— se quedan en un servidor que controlas, no en el panel de otro.
Lo que cuesta tenerlo en marcha
Langfuse v4 no es un binario pequeño, sino seis contenedores: la app web, un worker en segundo plano, Postgres, ClickHouse para los eventos, Redis y MinIO para los ficheros. Lo instalamos en un servidor de prueba y medimos unos 3,5 GB de RAM en uso con un proyecto vacío. Lo que crece es ClickHouse, porque ahí acaba cada span que envías.
| Carga | Plan |
|---|---|
| Un puñado de agentes, semanas de trazas | Medium-IP: 6 vCPU, 6 GB, 45 GB ($20/mes) |
| Una flota de agentes, meses de historial | Planes Pro, desde 32 GB de RAM — bajo solicitud |
En planes más pequeños se instala y luego sufre en cuanto ClickHouse y el worker quieren memoria a la vez. Yo no bajaría de 6 GB.
¿Dónde viven los agentes? Si corren en otras máquinas, elige un plan con su propia IPv4: necesitan un endpoint HTTPS, y en un plan NAT la dirección de salida no es tuya y desde fuera solo se llega al puerto SSH. Si los agentes corren en este mismo servidor, Langfuse nunca tiene que asomarse a internet: para una prueba basta Medium con NAT ($12/mes). Hay dos ubicaciones, Alemania y Finlandia; la disponibilidad se ve al hacer el pedido. Elige la más cercana a los agentes, no a ti. Los servidores se pagan con cripto, sin KYC.
Instalación
En un servidor Ubuntu 24.04 o Debian 12 recién creado, como root. Primero Docker:
curl -fsSL https://get.docker.com | sh
Después, Langfuse, con secretos reales en lugar de los valores por defecto del archivo compose:
git clone --depth 1 https://github.com/langfuse/langfuse.git
cd langfuse
PG=$(openssl rand -hex 16); MINIO=$(openssl rand -hex 16)
cat > .env <<EOF
NEXTAUTH_URL=https://langfuse.example.com
NEXTAUTH_SECRET=$(openssl rand -hex 32)
SALT=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$PG
DATABASE_URL=postgresql://postgres:$PG@postgres:5432/postgres
CLICKHOUSE_PASSWORD=$(openssl rand -hex 16)
MINIO_ROOT_PASSWORD=$MINIO
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=$MINIO
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=$MINIO
LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY=$MINIO
REDIS_AUTH=$(openssl rand -hex 16)
TELEMETRY_ENABLED=false
EOF
chmod 600 .env
Dos de esas contraseñas tienen pareja: la de Postgres también va dentro de DATABASE_URL, y la de MinIO tiene que coincidir con las tres líneas S3_..._SECRET_ACCESS_KEY. Por eso cada una sale de una sola variable.
Ahora, la trampa. El archivo compose publica la app web en el puerto 3000 y MinIO en el 9090 en todas las interfaces, y los puertos que publica Docker se saltan ufw. Ata ambos a localhost para que el proxy inverso sea la única entrada:
sed -i 's/- 3000:3000/- 127.0.0.1:3000:3000/; s/- 9090:9000/- 127.0.0.1:9090:9000/' docker-compose.yml
docker compose up -d
curl -s http://127.0.0.1:3000/api/public/health
El último comando debería mostrar {"status":"OK", ...}. El primer arranque ejecuta las migraciones de la base de datos; en nuestra prueba, el chequeo de salud pasó unos 15 segundos después de levantar los contenedores.
HTTPS delante
Apunta langfuse.example.com a la IP del servidor y luego:
apt install -y caddy
cat > /etc/caddy/Caddyfile <<'EOF'
langfuse.example.com {
reverse_proxy 127.0.0.1:3000
}
EOF
systemctl reload caddy
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
Caddy obtiene el certificado por su cuenta. Abre el sitio, crea tu cuenta (será la primera organización), añade un proyecto y copia sus claves pública y secreta.
Después, cierra la puerta al salir. Langfuse entiende AUTH_DISABLE_SIGNUP, pero el archivo compose de serie no se la pasa al contenedor web, así que hace falta una línea más bajo langfuse-web:
sed -i 's|^ NEXTAUTH_SECRET: \(.*\)$| NEXTAUTH_SECRET: \1\n AUTH_DISABLE_SIGNUP: ${AUTH_DISABLE_SIGNUP:-false}|' docker-compose.yml
echo "AUTH_DISABLE_SIGNUP=true" >> .env
docker compose up -d
A partir de ahora, un intento de registro recibe «Sign up is disabled». A tus compañeros añádelos desde dentro del proyecto.
Enviar trazas desde un agente
El SDK de Python envuelve tus funciones, y las llamadas anidadas se convierten en spans anidados:
python3 -m venv venv && . venv/bin/activate
pip install langfuse
export LANGFUSE_PUBLIC_KEY=pk-lf-...
export LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_BASE_URL=https://langfuse.example.com
from langfuse import get_client, observe
langfuse = get_client() # lee las variables LANGFUSE_*
@observe(name="fetch-prices")
def fetch_prices(pair: str) -> dict:
return {"pair": pair, "bid": 64210.5}
@observe(name="nightly-agent-run")
def run() -> str:
data = fetch_prices("BTC/USDT")
return f"done: {data['pair']}"
print(run())
langfuse.flush() # los scripts cortos terminan antes de que acabe el envío en segundo plano
No te saltes LANGFUSE_BASE_URL. Sin ella, el SDK envía todo en silencio a la nube de Langfuse, tus claves no existen allí y acabas mirando un panel vacío.
Para comprobar desde la consola que los spans llegaron:
curl -s -u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" \
"$LANGFUSE_BASE_URL/api/public/v2/observations?limit=5"
En v4, el antiguo endpoint /api/public/traces responde con un error sobre el modo events-only. Es lo esperado, no una instalación rota. En un agente real envuelves igual la llamada al LLM y cada herramienta; además, Langfuse trae integraciones para los SDK de LLM y frameworks de agentes más usados, así que muchas veces basta con cambiar un import en vez de llenar todo de decoradores.
Qué vigilar
- Disco. ClickHouse y MinIO guardan todo lo que envías.
docker system df -vmuestra adónde fue el espacio. Si trazas prompts y respuestas largos, los 45 GB de Medium-IP se llenan en semanas, no en años. Revísalo cada mes; cuando se te quede pequeño, hay un plan Pro de 400 GB o más bajo solicitud. - Actualizaciones. El archivo compose fija la versión mayor (
:4).docker compose pull && docker compose up -dtrae las correcciones dentro de ella. Lee el changelog antes de cambiar de versión mayor: justo en la v4 desapareció el endpoint de trazas. - Copias de seguridad. Lo importante vive en volúmenes de Docker: Postgres guarda usuarios, proyectos y claves; ClickHouse y MinIO, las trazas. Cópialos fuera del servidor de forma programada.
¿Vale la pena por $20 al mes? Si tus agentes gastan más que eso en tokens —y la mayoría de los agentes sin supervisión lo hacen—, saber adónde fueron los tokens se paga solo la primera vez que un bucle se desboca. Un agente pequeño que corre una vez al día no necesita nada de esto; con un fichero de log y un contador de tokens basta.
Relacionado
- VPS para agentes de IA: donde corren los propios agentes
- VPS para sistemas multiagente: dimensionar una flota entera
- VPS para un proxy LiteLLM: una pasarela para todas las claves de modelos, con límites de gasto
- Planes Pro bajo solicitud: de 32 a 80 GB de RAM para una flota de agentes
- Cómo configurar un cortafuegos ufw
Comentarios
Aún no hay comentarios. Sé el primero.