MAATRIX / Блог / Langfuse на сервере: частые ошибки и решения

Langfuse на сервере: частые ошибки и решения

Langfuse на сервере: частые ошибки и решения

MAATRIX

Self-hosted Langfuse — это минимум шесть контейнеров: web, worker, Postgres, ClickHouse, Redis и S3-совместимое хранилище, — и падение любого из них выглядит как невнятная ошибка на экране логина или пустой дашборд без единого трейса. Разбираем частые ошибки Langfuse на сервере по слоям: какой контейнер не поднялся, что написано в его логе и какой командой это проверить за секунды.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

С чего начать разбор ошибок Langfuse: три команды на диагностику

Прежде чем лезть в конфиги, узнайте, какой из шести контейнеров сломан: langfuse-web, langfuse-worker, Postgres, ClickHouse, Redis и S3-хранилище (обычно MinIO).

docker compose ps

Смотрите на STATUS, а не на сам факт «контейнер существует»: Up 2 minutes (unhealthy) и Restarting (1) 15 seconds ago — два разных класса проблем. Restarting значит, что процесс падает прямо при старте, и причина — в первых строчках лога:

docker compose logs --tail=50 langfuse-web

Третья команда — health-эндпоинт приложения, он проверяет реальные зависимости, а не просто «процесс жив»:

curl -s http://127.0.0.1:3000/api/public/health | jq

{"status":"OK","version":"3.xx.x"} значит, что web достучался до Postgres. curl: (7) Failed to connect to 127.0.0.1 port 3000 — процесс не слушает порт вообще, и до проверки зависимостей дело не дошло.

По тому, что показали три команды, проблема попадает в один из слоёв:

СимптомСлойГде искать
langfuse-web в Restarting, лог обрывается на стартеПеременные окружения webРаздел 2
Web поднялся, но логин зависает на редиректеNEXTAUTH / проксиРазделы 2 и 5
Дашборд открывается, но ошибки про relation или Code: 81Postgres / ClickHouseРаздел 3
langfuse-worker перезапускается каждые 10–30 секундRedis / памятьРаздел 4
Всё зелёное в docker compose ps, но трейсов в интерфейсе нетПриём событий на стороне SDKсм. отдельный разбор про пропавшие трейсы

Последняя строка — не отговорка: если инфраструктура здорова, а не долетают именно данные, это отдельная диагностика SDK и очереди приёма, а не поломка сервера. Здесь — только то, что ломает сам стек.

Web-контейнер не поднимается: NEXTAUTH, SALT, ENCRYPTION_KEY

langfuse-web требует пять обязательных переменных: DATABASE_URL, NEXTAUTH_URL, NEXTAUTH_SECRET, SALT, ENCRYPTION_KEY. Не хватает любой — контейнер не стартует: секреты по умолчанию разработчики Langfuse сознательно не подставляют.

Самая частая находка в логе — от NextAuth, на котором построена аутентификация:

[next-auth][error][NO_SECRET]
https://next-auth.js.org/errors#no_secret
Please define a `secret` in production.

Лечится одной строкой: NEXTAUTH_SECRET=$(openssl rand -base64 32). Тот же принцип для SALT — отдельное значение для хеширования, не то же самое, что NEXTAUTH_SECRET: перепутанные местами переменные не уронят контейнер сразу, а аукнутся позже, при сверке уже сохранённых хешей.

ENCRYPTION_KEY жёстче: значение обязано быть ровно 256 бит — 64 символа в hex, не больше и не меньше. openssl rand -hex 32 (не -base64) даёт нужный формат; ключ из base64 короче и содержит символы, недопустимые для hex, и контейнер откажется стартовать. Эта переменная шифрует хранимые в Postgres ключи провайдеров — потерять её после того, как в базе накопились такие записи, значит потерять возможность их расшифровать.

Отдельный случай — не падение, а зависание на входе. langfuse-web поднялся, /api/public/health отвечает OK, а страница логина после ввода пароля перезагружает саму себя без видимой ошибки. Причина почти всегда в NEXTAUTH_URL: значение должно быть тем адресом, который видит браузер снаружи — https://langfuse.example.com, а не http://langfuse-web:3000 и не http://localhost:3000 за Nginx. Проверка — вкладка Network в браузере: если редирект уводит на localhost или на http вместо https, дело в этом.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Langfuse

Postgres и ClickHouse: ошибки миграций и «relation does not exist»

langfuse-web сам прогоняет миграции Prisma на Postgres при старте. Не хватает прав у пользователя БД — например, урезанная учётка на managed-Postgres вместо владельца схемы — и миграция обрывается, а запрос к интерфейсу отвечает ошибкой вида:

PrismaClientKnownRequestError:
Invalid `prisma.project.findMany()` invocation:
The table `public.Project` does not exist in the current database.

Prisma сохраняет регистр имён моделей и берёт их в кавычки, поэтому в логе таблицы называются "Project", "ApiKey" — с большой буквы, не project/api_key. relation "Project" does not exist значит, что миграции не докатились: чините права пользователя БД или прогоняйте миграцию вручную, а не ищите баг в самом Langfuse.

С ClickHouse — путаница из-за двух переменных на два протокола. CLICKHOUSE_URL — HTTP-интерфейс, порт 8123, для обычных запросов приложения. CLICKHOUSE_MIGRATION_URL — нативный протокол, порт 9000, через него при старте прогоняются миграции схемы. Рабочий ClickHouse на 8123 и connect ECONNREFUSED 127.0.0.1:9000 в логе миграций — не противоречие: порты проверяются независимо, и в конфиге легко указать только один из них.

Code: 81. DB::Exception: Database langfuse_prod does not exist

— ClickHouse поднялся, а базы с нужным именем никто не создал: CLICKHOUSE_DB должна совпадать с тем, что реально есть на сервере, сам Langfuse её не создаёт.

Третья категория — не ошибка старта, а деградация со временем: ClickHouse хранит трейсы в виде партов, периодически сливает их (merge), и на тесном диске это кончается отказом писать новые данные:

DB::Exception: Not enough space to add ... GiB. Envisioned disk usage

Self-hosted Langfuse не чистит старые трейсы сам: автоудаления данных старше N дней в open-source версии нет, только вручную — ALTER TABLE ... DELETE по TTL или скрипт из крона. На активном проекте диск ClickHouse стоит проверять docker exec clickhouse df -h /var/lib/clickhouse, а не ждать, пока он забьётся сам.

Воркер падает или зависает: Redis, память и OOM

langfuse-worker обрабатывает очередь на BullMQ поверх Redis: без рабочего подключения он не держится стабильно, а перезапускается каждые 10–30 секунд. Классика Docker Compose — адрес Redis должен быть именем сервиса, а не localhost:

ioredis: connect ECONNREFUSED 127.0.0.1:6379

значит, в REDIS_HOST (или в REDIS_CONNECTION_STRING) стоит localhost, а Redis — отдельный контейнер redis, доступный из сети compose только по имени сервиса. Если адрес правильный, а ошибка про авторизацию —

ReplyError: NOAUTH Authentication required.

— у Redis включён requirepass, а у worker либо не задан REDIS_AUTH, либо задан со значением, не совпадающим с конфигом Redis.

Второй класс — не «упал с понятной ошибкой», а «пропал молча». Шесть процессов на одной машине, и ClickHouse отдельно любит забирать память под кэш и буферы вставки при всплеске нагрузки. Контейнер в Exited (137) при тишине в собственном логе ровно там, где должна быть ошибка, — это код SIGKILL (128 + 9), и почти всегда за ним стоит OOM killer ядра, а не баг приложения:

dmesg -T | grep -i "killed process"
docker inspect langfuse-worker --format='{{.State.OOMKilled}}'

true во втором выводе закрывает вопрос: памяти физически не хватило. Перезапускать контейнер на той же RAM — не решение; решение — развести сервисы по разным машинам либо увеличить память сервера целиком (раздел 7 — сколько её закладывать).

Nginx перед Langfuse: 413, 502 и петля редиректов на HTTPS

Приём событий у Langfuse идёт батчами: SDK копит несколько observation'ов и одним POST скидывает их на /api/public/ingestion, а не шлёт каждый трейс отдельно. Мультимодальные данные — картинки, аудио в base64 — легко перерастают лимит Nginx в 1 МБ, и запрос обрывает сам прокси, не дожидаясь приложения:

413 Request Entity Too Large

в ответе клиенту и client intended to send too large body в /var/log/nginx/error.log. Правка — одна строка:

client_max_body_size 20m;

Второй частый гость — 502 Bad Gateway: апстрим не успел ответить вовремя, выборка большого диапазона трейсов из ClickHouse или тяжёлая batch-вставка легко превышает стандартные 60 секунд. Таймауты стоит поднять явно:

proxy_read_timeout 120s;
proxy_send_timeout 120s;

Третья категория — не ошибка, а бесконечный редирект на логин, если Nginx терминирует HTTPS, а до langfuse-web запрос доходит уже как http. NextAuth считает соединение небезопасным, не ставит secure-cookie, и браузер каждый раз просит войти заново. Без этих заголовков NEXTAUTH_URL не спасает:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;

Docker Compose и апдейты: переменные, которые «не применились»

Частый тупик после правки .env: значения меняете, docker compose restart langfuse-web выполняете — а поведение не меняется. restart перезапускает уже существующий контейнер с уже подставленным при создании окружением, .env заново он не перечитывает. Правильная команда —

docker compose up -d --force-recreate langfuse-web

пересоздаёт контейнер с текущими значениями; без имени сервиса docker compose up -d пересоздаст все контейнеры, чей конфиг изменился.

Отдельная ловушка — переменные LANGFUSE_INIT_ORG_ID, LANGFUSE_INIT_USER_EMAIL, LANGFUSE_INIT_USER_PASSWORD: создают организацию, проект и администратора автоматически при пустой базе Postgres, но срабатывают ровно один раз. Поменяли LANGFUSE_INIT_USER_PASSWORD и перезапустили контейнер на базе, где организация уже создана, — ничего не произойдёт: entrypoint видит существующие записи и молча пропускает инициализацию, хотя кажется, что пароль должен был смениться.

Рядом — честный, не гипотетический риск: по умолчанию self-hosted Langfuse пускает регистрацию через /auth/sign-up кого угодно с сетевым доступом до домена. Создали первого администратора — сразу выставляйте AUTH_DISABLE_SIGNUP=true: открытая форма на боевом инстансе с чужими трейсами (а там может быть что угодно, вплоть до персональных данных) — не баг, а дыра, которую оставили сами.

Последнее — обновление образа. langfuse/langfuse:latest без фиксации версии однажды перепрыгнет архитектурную границу: переход с 2.x на 3.x сделал ClickHouse и S3-хранилище обязательными вместо опциональных, и контейнер без них на latest откажется стартовать вместо тихой работы по-старому. Фиксируйте версию тегом в docker-compose.yml, поднимайте осознанно — читая changelog, а не при каждом docker compose pull.

Какой сервер под Langfuse брать в MAATRIX

Часть ошибок из этого текста — следствие того, что шесть сервисов ужимаются на тесной по ресурсам машине: ClickHouse режет кэш, диск забивается партами без встроенной чистки, воркер получает OOM первым — стартует последним и претендует на память, которой уже не осталось.

Честный минимум: 4 vCPU, 8 ГБ RAM, 80 ГБ NVMe. Хватает поднять весь стек — web, worker, Postgres, ClickHouse, Redis, MinIO — и пользоваться Langfuse одному или небольшой командой с умеренным потоком трейсов. Ниже 8 ГБ совокупно на шесть контейнеров велик риск того же OOM, что разбирали в разделе 4: ClickHouse при мердже партов и Postgres при батче миграций одновременно просят память, а делить им особо нечего. 80 ГБ NVMe — запас на несколько месяцев логирования, прежде чем встанет вопрос ретеншена вручную.

Комфортный вариант: 8 vCPU, 16–32 ГБ RAM, 150–200 ГБ NVMe. Точная цифра зависит от объёма LLM-трафика, который вы логируете: чем больше трейсов в день, тем больше ClickHouse хочет держать в кэше ради быстрых выборок в дашборде. Такая конфигурация переживает всплески без перезапусков воркера и без еженедельной слежки за диском.

В каталоге приложений MAATRIX Langfuse разворачивается автоматически при заказе сервера — весь стек из шести контейнеров поднимается сразу настроенным, без ручного docker compose up, а адрес панели и ключи доступа появляются в личном кабинете, в разделе «Доступ». Работает на Ubuntu и на Debian; дальше остаётся привязать домен, включить HTTPS и, если разворачиваете с нуля, сразу выставить AUTH_DISABLE_SIGNUP=true — привычка из раздела 6, которая экономит нервы позже.

Локация — Великобритания (Лондон). В трейсах Langfuse оседают промпты и ответы пользователей — по сути, копия переписки с вашим ИИ-продуктом вместе с любыми персональными данными, которые в неё попали. Команде на европейскую аудиторию держать эту копию в юрисдикции по соседству с GDPR — осознанный выбор, а не формальность. Плюс короткий пинг от серверов приложения в ЕС: batch-вставки идут чаще, чем кажется, и разница в RTT заметна на отзывчивости дашборда.

Оплата — картой российского банка, СБП, криптовалютой или токеном MAAT; иностранная карта не нужна, хотя сервер физически стоит в Лондоне.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Langfuse

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

docker compose ps показывает langfuse-web в вечном Restarting — с чего начать?

Смотрите первые строки docker compose logs langfuse-web: контейнер падает раньше, чем успевает вывести что-то, кроме стектрейса нехватающей переменной. Проверьте DATABASE_URL, NEXTAUTH_SECRET, SALT, ENCRYPTION_KEY, NEXTAUTH_URL.

После входа страница логина сама перезагружается, ошибки не видно — что не так?

Чаще всего NEXTAUTH_URL не совпадает с реальным внешним адресом, либо Nginx не передаёт X-Forwarded-Proto, и secure-cookie не выставляется. Откройте Network в браузере: редирект на localhost или на http вместо https подтверждает диагноз.

Забыли пароль администратора, а «забыли пароль» ничего не присылает на почту — как быть?

У self-hosted Langfuse нет почты из коробки: письмо со сбросом уходит только если заданы переменные SMTP-подключения и EMAIL_FROM_ADDRESS. Без них форма молча ничего не отправляет. Быстрее всего — обновить пароль напрямую в Postgres, либо, если данные ещё не жалко, поднять базу заново и завести администратора через LANGFUSE_INIT_USER_EMAIL/LANGFUSE_INIT_USER_PASSWORD на пустой БД.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.