PostHog на сервере: частые ошибки и решения
PostHog — это воронки, сессии, feature flags и A/B-тесты в одном self-hosted инструменте, но именно эта «всё в одном» архитектура и создаёт большинство проблем: под капотом у него не один сервис, а связка из ClickHouse, PostgreSQL, Redis, Kafka (или его облегчённой замены) и десятка контейнеров-воркеров. Если сервер поднят на глазок, без запаса по памяти и диску, PostHog начинает вести себя странно — события теряются, дашборды показывают ноль, контейнеры падают по OOM. Разберём конкретные симптомы и что с ними делать.
Содержание
- Почему PostHog требует больше ресурсов, чем кажется
- ClickHouse не стартует или падает при первом запуске
- События приходят, но не появляются в дашбордах
- OOM-киллер убивает контейнеры под нагрузкой
- Feature flags и A/B-тесты не обновляются у клиентов
- Резервное копирование и восстановление без потери событий
- Обновление PostHog без простоя аналитики
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Почему PostHog требует больше ресурсов, чем кажется
Официальный docker-compose разворачивает не веб-приложение с базой, а мини-кластер: clickhouse, postgres, redis, kafka (или redpanda в новых версиях), plugin-server (обработка событий и вебхуков), несколько worker-контейнеров и сам web. Даже в состоянии простоя это отъедает 3-4 ГБ RAM только на инфраструктуру, до самой обработки трафика.
Минимальные требования, с которыми PostHog не будет постоянно падать в проде:
| Нагрузка | RAM | CPU | Диск |
|---|---|---|---|
| Тест/до 10k событий/день | 8 ГБ | 4 vCPU | 60 ГБ SSD |
| Продакшен, 100k-500k событий/день | 16 ГБ | 6-8 vCPU | 150+ ГБ NVMe |
| Высокая нагрузка, млн+ событий/день | 32 ГБ+ | 12+ vCPU | отдельный ClickHouse-кластер |
Если вы ставили PostHog на 4 ГБ «попробовать» и он посыпался — это не баг, это ожидаемое поведение. Для стабильного теста и тем более прода закладывайте аренду сервера с запасом хотя бы вдвое больше формальных минимумов из документации — под пики нагрузки и обновления.
ClickHouse не стартует или падает при первом запуске
Самая частая ошибка новичков — ClickHouse тихо умирает через несколько секунд после docker compose up, а в логах веб-контейнера летят таймауты подключения к порту 9000/8123. Проверяем причину:
docker compose logs clickhouse --tail=100
Типичные находки и решения:
Cannot allocate memory/ контейнер убит OOM-киллером — ClickHouse агрессивно резервирует память под кэши. На серверах с 8 ГБ и меньше ограничьте его явно вdocker-compose.yml:
clickhouse:
deploy:
resources:
limits:
memory: 3g
и добавьте в конфиг ClickHouse max_server_memory_usage_to_ram_ratio: 0.7, иначе он снова попытается забрать почти всю память хоста.
Too many open files— поднимите лимиты на хосте:
ulimit -n 262144
echo "* soft nofile 262144" >> /etc/security/limits.conf
echo "* hard nofile 262144" >> /etc/security/limits.conf
и перезапустите Docker: systemctl restart docker.
- Несовместимая версия ядра с
vm.max_map_count— на некоторых минимальных образах (в частности часть Alpine-based VPS-шаблонов) значение занижено:
sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
Проверить, что ClickHouse реально принимает соединения, можно напрямую:
docker compose exec clickhouse clickhouse-client --query "SELECT 1"
Если это работает, а PostHog всё равно ругается — проблема в сетевой видимости контейнеров, смотрите следующий раздел.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверСобытия приходят, но не появляются в дашбордах
Это второй по частоте кейс: SDK отправляет запросы, plugin-server логирует их получение, а в интерфейсе — пусто или задержка в часы. Причины почти всегда в очереди между plugin-server и ClickHouse:
- Kafka/Redpanda не успевает разгребать очередь. Смотрите лаг потребителя:
docker compose exec kafka kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--describe --group clickhouse-ingestion
Если LAG растёт, а не уменьшается — plugin-server не справляется с обработкой. Увеличьте число воркеров в .env:
PLUGIN_SERVER_INGESTION_WORKERS=4
и перезапустите plugin-server.
- Часовой пояс сервера расходится с тем, что ждёт браузер дашборда. PostHog хранит время событий в UTC, но если на хосте
dateпоказывает не то — фильтры «последние 24 часа» рисуют пустоту, хотя события есть. Проверьте:
timedatectl
и приведите к UTC (timedatectl set-timezone UTC), не полагаясь на локальный часовой пояс контейнеров.
- Materialized views ClickHouse не досчитаны после миграции. После обновления версии PostHog иногда нужно вручную прогнать миграции ClickHouse:
docker compose run --rm web python manage.py migrate_clickhouse
Без этого часть агрегированных отчётов (особенно ретеншн и воронки) молчит, хотя сырые события в таблице events лежат корректно — это легко проверить прямым запросом:
SELECT count() FROM events WHERE timestamp > now() - INTERVAL 1 DAY;
OOM-киллер убивает контейнеры под нагрузкой
Если docker compose ps показывает, что worker или plugin-server регулярно рестартуют, а dmesg | grep -i oom подтверждает — сервер физически не тянет. Порядок действий:
- Посмотрите текущее потребление по контейнерам, не по хосту в среднем:
docker stats --no-stream
- Задайте лимиты каждому сервису в compose-файле, чтобы один прожорливый контейнер не убивал соседей через system OOM, а получал контролируемый рестарт:
worker:
deploy:
resources:
limits:
memory: 1.5g
reservations:
memory: 512m
- Включите swap как страховку от жёсткого падения (не как постоянное решение — это костыль для пиков):
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Если после лимитов и свопа падения продолжаются на стабильной нагрузке — это не настройка, а недостаток железа, и апгрейд плана сервера дешевле часов, потраченных на попытки выжать невозможное из 8 ГБ. Подробнее про мониторинг таких падений в реальном времени — в статье про Grafana и Prometheus на сервере.
Feature flags и A/B-тесты не обновляются у клиентов
Отдельная головная боль — флаги, которые вы поменяли в интерфейсе, не долетают до фронтенда или мобильного SDK. Здесь три обычных подозреваемых:
- Redis недоступен или переполнен. PostHog кеширует вычисленные флаги в Redis с коротким TTL, и если Redis падает или упирается в
maxmemory, SDK откатывается на устаревший локальный кеш. Проверьте:
docker compose exec redis redis-cli INFO memory | grep used_memory_human
docker compose exec redis redis-cli CONFIG GET maxmemory-policy
Для PostHog политика вытеснения должна быть allkeys-lru, иначе Redis начнёт отказывать в записи новых ключей при заполнении, вместо вытеснения старых.
- SDK держит клиент дольше, чем ждёт пользователь. По умолчанию JS SDK кеширует флаги на клиенте и обновляет их по своему циклу, а не мгновенно после изменения на сервере — это нормальное поведение, не баг PostHog, но частая причина тикетов «флаг не сработал».
- CDN или reverse-proxy кеширует ответ
/decide/. Если PostHog стоит за Nginx или Caddy с общим кешем статики, endpoint/decide/(отдающий актуальные флаги) может случайно попасть под правило кеширования. Явно исключите его:
location /decide/ {
proxy_cache off;
proxy_pass http://posthog_backend;
}
Если вы настраиваете proxy с нуля, посмотрите на разницу подходов в статье Caddy или Nginx — что выбрать для сервера.
Резервное копирование и восстановление без потери событий
PostHog хранит данные в двух местах с разной логикой бэкапа, и это частый источник паники при восстановлении: люди бэкапят только Postgres (пользователей, проекты, настройки флагов), забывая про ClickHouse (сами события), — и после восстановления получают рабочий интерфейс с пустой историей.
Бэкап PostgreSQL:
docker compose exec postgres pg_dump -U posthog posthog > posthog_pg_$(date +%F).sql
Бэкап ClickHouse (через встроенный clickhouse-backup или прямой BACKUP):
docker compose exec clickhouse clickhouse-client --query \
"BACKUP DATABASE posthog TO Disk('backups', 'posthog_$(date +%F)')"
Оба бэкапа нужно снимать синхронно (в одном скрипте, одним запуском cron), иначе при восстановлении получите рассинхрон: события с датами, для которых уже нет соответствующих проектов/пользователей в Postgres, или наоборот. Складывайте архивы за пределы того же диска — на S3-совместимое хранилище или соседний сервер, чтобы падение основного диска не унесло и бэкапы с собой.
Обновление PostHog без простоя аналитики
PostHog выпускает обновления часто, и прыжок через несколько минорных версий разом — почти гарантированный способ словить необработанную миграцию ClickHouse. Безопасный порядок:
- Снимите бэкап Postgres и ClickHouse (см. выше) — это не опция, а обязательный шаг перед любым обновлением.
- Проверьте changelog именно вашей текущей и целевой версии на breaking changes в схеме — PostHog иногда переименовывает таблицы или меняет формат plugin-конфигов.
- Обновляйте образы по одному минорному релизу за раз, а не сразу на последний:
docker compose pull
docker compose up -d
docker compose logs -f web plugin-server
- После старта прогоните миграции явно, не полагаясь только на автозапуск:
docker compose run --rm web python manage.py migrate
docker compose run --rm web python manage.py migrate_clickhouse
- Отправьте тестовое событие и убедитесь, что оно долетело до дашборда, прежде чем считать апгрейд завершённым.
Если что-то пошло не так на середине миграции ClickHouse — откатываться проще из бэкапа, чем пытаться руками чинить частично применённую схему; на такую починку обычно уходит больше времени, чем на само восстановление.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Сколько реально нужно RAM для PostHog в проде, а не по документации?
Для стабильной работы с полноценной обработкой событий закладывайте от 16 ГБ даже на средний трафик — 8 ГБ хватает только для теста и быстро упирается в OOM при первой нагрузке.
Можно ли обойтись без ClickHouse и упростить стек?
Нет, ClickHouse — обязательная часть self-hosted PostHog для аналитики событий; облачная версия PostHog Cloud снимает эту заботу, но тогда речь не о self-hosted решении на своём сервере.
Почему после переезда на новый сервер пропала часть исторических событий?
Почти всегда потому, что мигрировали только том PostgreSQL, а данные ClickHouse (реальная история событий) остались на старом хосте — переносить нужно оба хранилища, желательно через штатный бэкап/рестор, а не копированием файлов вручную.
PostHog тормозит при построении сложных воронок — это нормально?
На больших объёмах событий и без выделенных ресурсов под ClickHouse тяжёлые запросы с несколькими шагами воронки и большим окном времени действительно могут занимать заметное время — это ограничение самой аналитической модели, а не обязательно неправильная настройка; частично помогает увеличение ресурсов ClickHouse и добавление материализованных представлений под часто используемые отчёты.
Стоит ли ставить PostHog в Kubernetes вместо docker-compose?
Для небольших и средних инсталляций docker-compose проще в поддержке и достаточно надёжен; Kubernetes имеет смысл, когда вы уже упираетесь в горизонтальное масштабирование ClickHouse или plugin-server и готовы нести сложность оркестрации ради этого.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →