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

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

MAATRIX

Formbricks — self-hosted платформа для форм и опросов с логикой ветвления, условными переходами и встраиванием в продукт: разумная альтернатива Typeform или Tally для тех, кто не хочет отдавать данные респондентов на сторонний SaaS. Разворачивается он через Docker Compose и в целом ставится за 10 минут — но именно из-за большого количества переменных окружения (шифрование, аутентификация, почта, домен) новички регулярно упираются в одни и те же ошибки. Ниже — разбор самых частых проблем и как их закрыть.

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

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

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

Как разворачивают Formbricks: Docker Compose и переменные окружения

Официальный способ — Docker Compose. Минимальный набор сервисов: сам formbricks (Next.js-приложение) и postgres для хранения данных. Базовый файл выглядит примерно так:

services:
  formbricks:
    image: formbricks/formbricks:latest
    restart: always
    depends_on:
      - postgres
    ports:
      - "3000:3000"
    environment:
      WEBAPP_URL: "https://forms.example.com"
      NEXTAUTH_URL: "https://forms.example.com"
      NEXTAUTH_SECRET: "${NEXTAUTH_SECRET}"
      ENCRYPTION_KEY: "${ENCRYPTION_KEY}"
      CRON_SECRET: "${CRON_SECRET}"
      DATABASE_URL: "postgresql://formbricks:${POSTGRES_PASSWORD}@postgres:5432/formbricks?schema=public"
    volumes:
      - uploads:/home/nextjs/apps/web/uploads/

  postgres:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_USER: formbricks
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
      POSTGRES_DB: formbricks
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  uploads:
  postgres_data:

Секреты (NEXTAUTH_SECRET, ENCRYPTION_KEY, CRON_SECRET) генерируются командой:

openssl rand -hex 32

Их нужно ровно три, и все три — разные строки. Складывать их в .env рядом с docker-compose.yml удобнее, чем прописывать прямо в файле — так секреты не улетят в git при случайном коммите.

Перед проектом стоит поднять сервер с достаточным запасом памяти: на 1-2 активных формах хватит 2 ГБ ОЗУ, но Next.js в режиме сборки и Prisma-миграции при апдейтах любят прожорливые пики — с 4 ГБ живётся спокойнее. Подробнее о том, как вообще готовить сервер под продакшен-докер-стек, — в статье про Docker Compose в проде.

Контейнер падает или не стартует: NEXTAUTH_SECRET, ENCRYPTION_KEY, WEBAPP_URL

Самая частая причина, по которой formbricks контейнер уходит в restart loop сразу после docker compose up -d, — не заданные или пустые переменные шифрования и аутентификации. Приложение при старте проверяет их наличие и падает с понятной, но легко пропускаемой в логах ошибкой:

docker compose logs formbricks --tail=50

Смотрите на строки вида Error: ENCRYPTION_KEY is not set или NEXTAUTH_SECRET must be a 32-byte hex string. Типичные грабли:

  • Забыли создать .env-файл — Compose подставляет пустые значения вместо ${NEXTAUTH_SECRET}, приложение падает на старте.
  • Секрет неправильной длиныENCRYPTION_KEY должен быть ровно 32 байта в hex (64 символа), сокращённая или скопированная с обрезкой строка не пройдёт валидацию.
  • WEBAPP_URL не совпадает с NEXTAUTH_URL — Formbricks требует их синхронности, иначе auth-редиректы уходят не туда, а сессии не создаются даже без явного краша.
  • URL с завершающим слэшемhttps://forms.example.com/ вместо https://forms.example.com иногда ломает построение внутренних ссылок в письмах и виджете.

После правки .env контейнер нужно не просто перезапустить, а пересоздать, чтобы переменные подхватились:

docker compose up -d --force-recreate formbricks

Если падение происходит не сразу, а через 10-20 секунд после старта — почти всегда это уже следующая проблема, с базой данных.

Нужен сервер под эту задачу?

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

Арендовать сервер

Ошибки подключения к PostgreSQL и миграций

Formbricks использует Prisma для миграций схемы, и они выполняются автоматически при первом старте контейнера. Здесь ловят три типа ошибок:

ECONNREFUSED на порт 5432. Контейнер formbricks стартует быстрее, чем Postgres успевает поднять сокет. depends_on в Compose гарантирует только порядок запуска контейнеров, а не готовность сервиса внутри. Решение — health check на постгресе:

postgres:
  # ...
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U formbricks"]
    interval: 5s
    timeout: 5s
    retries: 10

formbricks:
  depends_on:
    postgres:
      condition: service_healthy

Неверный DATABASE_URL. Частая опечатка — использовать localhost вместо имени сервиса postgres внутри строки подключения. Внутри Docker-сети контейнеры видят друг друга по имени сервиса, а не по localhost (это порт хоста, не контейнера). Проверить фактическое имя пользователя и БД, с которыми поднялся Postgres:

docker compose exec postgres psql -U formbricks -d formbricks -c "\conninfo"

Миграция зависла или упала посередине. Если под капотом уже была база от старой версии Formbricks и вы обновились на мажорную версию, миграция может требовать ручного вмешательства. Смотрите полный лог:

docker compose logs formbricks | grep -i prisma

и сверяйтесь с changelog конкретной версии на GitHub перед апдейтом — большие мажорные релизы (переход между схемами базы) иногда описывают отдельный порядок действий.

SSL, reverse proxy и редирект-луп за Traefik/Nginx

Formbricks сам по себе слушает на 3000 порту без TLS — шифрование навешивает внешний reverse proxy. Тут возникает классическая для Next.js-приложений проблема: ERR_TOO_MANY_REDIRECTS или бесконечный редирект на /auth/login.

Причина почти всегда одна — рассинхрон между тем, что видит браузер (https://forms.example.com), и тем, что думает о себе приложение. Если WEBAPP_URL/NEXTAUTH_URL указывают на http://, а прокси терминирует TLS и проксирует на 3000 порт по HTTP, NextAuth генерирует secure-cookie для https-контекста, но само приложение считает себя http — и куки, и редиректы начинают противоречить друг другу.

Что проверить:

  • WEBAPP_URL и NEXTAUTH_URL — строго https://, без порта, без слэша в конце.
  • Прокси должен пробрасывать заголовки X-Forwarded-Proto: https и X-Forwarded-Host, иначе Next.js не поймёт, что запрос пришёл по HTTPS.

Пример для Nginx:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Если используете Traefik, у него X-Forwarded-Proto проставляется автоматически при правильно настроенном entrypoint с TLS — но стоит явно проверить это в браузере через DevTools → Network → Headers на запросе к /api/auth/session. Разницу между Traefik и Nginx Proxy Manager для такого сценария разбирали в статье про выбор reverse proxy для сервера. Сам сертификат и его продление — отдельная больная тема, если раньше не настраивали автопродление, посмотрите разбор частых ошибок Let's Encrypt.

Не приходят email-уведомления: настройка SMTP

Formbricks отправляет письма — подтверждение регистрации, уведомления о новых ответах, приглашения в команду — через SMTP, который нужно настроить отдельно. Без него аккаунт можно создать, но письма верификации просто не придут, и часть функций (например, приглашение коллег) не заработает.

Переменные окружения для SMTP:

SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE_ENABLED=0
SMTP_USER=noreply@example.com
SMTP_PASSWORD=your-password
MAIL_FROM=noreply@example.com

Частые ошибки:

  • Порт 587 без SMTP_SECURE_ENABLED — на 587 нужен STARTTLS, а не прямой TLS-коннект; если приложение пытается сразу шифровать соединение как на 465-м порту, будет ECONNRESET или таймаут.
  • Провайдер блокирует внешний SMTP от VPS — некоторые почтовые сервисы (в том числе многие бесплатные ящики) режут отправку с новых IP или требуют отдельного разрешения "App Password" вместо обычного пароля от ящика.
  • Нет обратного DNS (PTR) на IP сервера — письма уходят, но падают в спам или вовсе отклоняются принимающей стороной. Это отдельная настройка на уровне провайдера сервера, не Formbricks.

Если поднимаете почту с нуля именно под такие уведомления, проще и надёжнее не городить свой Postfix ради одного сервиса, а взять транзакционного провайдера (Resend, Postmark, SMTP от Яндекс 360 и т.п.) — так письма стабильнее долетают. Но если инфраструктура уже есть, логика настройки почтового сервера на VPS та же, что описана в статье про настройку Postfix.

Проверить SMTP можно и напрямую из контейнера, не дожидаясь реального действия в UI:

docker compose exec formbricks node -e "
const nodemailer = require('nodemailer');
const t = nodemailer.createTransport({host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, auth: {user: process.env.SMTP_USER, pass: process.env.SMTP_PASSWORD}});
t.verify().then(console.log).catch(console.error);
"

Обновление, бэкапы и нехватка памяти

Обновление. Тег latest в образе — не лучшая практика для продакшена: он может подтянуть мажорный релиз с изменением схемы базы без предупреждения. Разумнее закрепить конкретную версию (formbricks/formbricks:v3.x.x) и обновляться осознанно, читая release notes перед каждым шагом:

docker compose pull formbricks
docker compose up -d formbricks

Перед любым обновлением — снимок базы:

docker compose exec postgres pg_dump -U formbricks formbricks > formbricks_backup_$(date +%F).sql

Бэкапы volume'ов. Кроме базы, отдельного внимания требует volume с загруженными файлами (uploads) — картинки в вопросах, логотипы. Если он живёт только в Docker volume без внешнего бэкапа, потеря диска сервера означает потерю и базы, и всех вложений разом. Общий подход к бэкапу Docker-volume'ов на сервере разобран в статье про бэкап Docker volume.

Нехватка памяти при сборке/миграции. На серверах с 1-2 ГБ ОЗУ Next.js-приложение может уходить в OOM-kill именно в момент миграции или прогрева кэша после рестарта — в логах системы это видно как Out of memory: Killed process в dmesg или journalctl -k. Решения — либо увеличить объём сервера, либо временно добавить swap:

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

Swap не заменяет нормальную оперативку под нагрузкой, но снимает разовые пики при рестартах и обновлениях.

Нужен сервер под эту задачу?

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

Арендовать сервер

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

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

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

Formbricks можно развернуть без Docker?

Официально поддерживается только Docker/Docker Compose — это самый предсказуемый путь. Ручная установка Node.js-приложения с отдельным Postgres теоретически возможна, но вы теряете воспроизводимость окружения и усложняете себе апдейты.

Нужен ли Redis для self-hosted Formbricks?

В некоторых версиях Redis используется для rate limiting и кэша; если переменная подключения к Redis не задана, приложение в большинстве случаев просто отключает эту функциональность и работает без него — но для точного поведения конкретной версии сверяйтесь с её документацией на docs.formbricks.com.

Почему после обновления формы перестали открываться на встроенном виджете?

Проверьте, не изменился ли формат WEBAPP_URL или CORS-настройки между версиями — иногда мажорные релизы меняют домен, с которого разрешена встройка виджета на сторонние сайты.

Можно ли запустить несколько организаций (multi-tenant) на одном инстансе?

Да, Formbricks поддерживает несколько организаций и проектов в одной инсталляции — это не требует отдельных контейнеров, разграничение происходит на уровне приложения и базы.

Как перенести Formbricks на другой сервер?

Снять pg_dump базы, скопировать volume uploads, поднять тот же docker-compose.yml с теми же .env-секретами (особенно ENCRYPTION_KEY — без него зашифрованные в базе данные не расшифровать) на новом сервере и восстановить дамп.

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

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

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