Wraps the Langfuse client to snapshot prompts to disk at startup. If Langfuse is unreachable at runtime, the local backup is used as fallback.
How it works
new FrozenLangfuse({ promptsBackupPath }).bootstrap() creates a Langfuse client and runs the backup process:
- Backup file already exists → skip if
overwriteis false (log and continue) - Backup file missing → fetch all prompts from Langfuse, write to disk
- Fetch fails → retry with exponential backoff, throw
Errorafter max retries
At runtime, FrozenLangfuse proxies prompt.get() to inject the backup as fallback so the Langfuse SDK handles outages gracefully.
Installation
pnpm add langfuse-freezeUsage
Creating a backup (deploy / build time)
Use the constructor with bootstrap() to fetch and persist all labeled prompts from the Langfuse API:
import { FrozenLangfuse } from "langfuse-freeze"
const client = new FrozenLangfuse({
promptsBackupPath: "./langfuse-backup/prompts.json",
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
secretKey: process.env.LANGFUSE_SECRET_KEY,
baseUrl: process.env.LANGFUSE_HOST,
})
await client.bootstrap()Loading a pre-existing backup (runtime)
Use fromBackup() when the backup is expected to already exist. This throws immediately if the file is missing — making misconfiguration obvious at startup rather than at the first prompt fetch:
import { FrozenLangfuse } from "langfuse-freeze"
const client = FrozenLangfuse.fromBackup("./langfuse-backup/prompts.json", {
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
secretKey: process.env.LANGFUSE_SECRET_KEY,
baseUrl: process.env.LANGFUSE_HOST,
})
const prompt = await client.prompt.get("my-prompt", { type: "text", label: "production" })Drop-in replacement for LangfuseClient. Same API.
Bootstrap at container build time
The intended use is to run the CLI as a build step in your app’s Dockerfile, baking the backup into the image. langfuse-freeze must be listed as a dependency of your app.
FROM node:22-alpine AS base
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# Fetch all prompts from Langfuse and write the backup file.
# Credentials are passed as build secrets so they never appear in image layers.
RUN --mount=type=secret,id=langfuse_public_key \
--mount=type=secret,id=langfuse_secret_key \
--mount=type=secret,id=langfuse_host \
LANGFUSE_PUBLIC_KEY=$(cat /run/secrets/langfuse_public_key) \
LANGFUSE_SECRET_KEY=$(cat /run/secrets/langfuse_secret_key) \
LANGFUSE_HOST=$(cat /run/secrets/langfuse_host) \
npx langfuse-freeze --backup-path ./langfuse-backup/prompts.json
CMD ["node", "dist/index.js"]Build passing credentials from your local environment:
docker build \
--secret id=langfuse_public_key,env=LANGFUSE_PUBLIC_KEY \
--secret id=langfuse_secret_key,env=LANGFUSE_SECRET_KEY \
--secret id=langfuse_host,env=LANGFUSE_HOST \
-t myapp .Then in your app use fromBackup() — it hard-fails at startup if the backup wasn’t baked in:
import { FrozenLangfuse } from "langfuse-freeze"
const client = FrozenLangfuse.fromBackup("./langfuse-backup/prompts.json")
const prompt = await client.prompt.get("my-prompt", { type: "text" })CLI flags (all fall back to env vars if omitted):
--backup-path path to write the backup file (required)
--public-key LANGFUSE_PUBLIC_KEY
--secret-key LANGFUSE_SECRET_KEY
--host LANGFUSE_HOST
Then at application runtime load the backup with fromBackup().
bootstrap() options
await client.bootstrap({
overwrite: true, // re-fetch even if backup already exists (default: false)
maxRetries: 5, // fetch attempts before throwing (default: 3)
retryDelay: 1, // base seconds for exponential backoff (default: 2)
})Backup format
{
"my-prompt": {
"type": "text",
"labels": {
"production": "You are a helpful assistant.",
"dev": "You are a dev assistant."
}
}
}To refresh the backup, call bootstrap({ overwrite: true }) or delete the file and re-run the CLI.
Running tests
Unit tests (no network):
pnpm testE2E tests require Langfuse running at http://localhost:10016. We recommend using docker-compose.
Set the following in your .env to seed a local instance:
LANGFUSE_INIT_ORG_ID=my-org
LANGFUSE_INIT_PROJECT_ID=my-project
LANGFUSE_INIT_PROJECT_PUBLIC_KEY=pk-lf-1234
LANGFUSE_INIT_PROJECT_SECRET_KEY=sk-lf-1234
LANGFUSE_INIT_USER_EMAIL=user@example.com
LANGFUSE_INIT_USER_PASSWORD=password123
Start Langfuse:
docker compose --env-file .env upThen run E2E tests:
pnpm test:e2eLocal demo
A demo.mjs script lets you manually verify the fallback behaviour end-to-end:
node --env-file=.env demo.mjsIt bootstraps from a live Langfuse instance, pauses so you can stop Langfuse, then calls prompt.get() to confirm the backup fallback works while the service is unreachable.