mirror of
https://github.com/Dokploy/templates.git
synced 2026-07-16 03:15:23 +02:00
Merge pull request #1028 from Dokploy/feat/posthog
feat: add PostHog template
This commit is contained in:
646
blueprints/posthog/docker-compose.yml
Normal file
646
blueprints/posthog/docker-compose.yml
Normal file
@@ -0,0 +1,646 @@
|
||||
# PostHog self-hosted ("hobby" deployment, trimmed for a single VPS).
|
||||
# Based on https://github.com/PostHog/posthog/blob/master/docker-compose.hobby.yml
|
||||
# Omitted vs upstream hobby (heavy/optional): Temporal + Elasticsearch (data
|
||||
# warehouse / batch exports), browserless (image exports / heatmap screenshots),
|
||||
# personhog, cymbal + error-tracking/logs/traces ingestion, capture-ai and
|
||||
# capture-logs. Everything needed for product analytics, feature flags,
|
||||
# surveys, session replay and the CDP pipeline is included.
|
||||
|
||||
x-logging: &default-logging
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "3"
|
||||
|
||||
x-django-env: &django-env
|
||||
# Postgres
|
||||
DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
PGHOST: db
|
||||
PGUSER: posthog
|
||||
PGPASSWORD: posthog
|
||||
PERSONS_DB_WRITER_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
PERSONS_DB_READER_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
# ClickHouse (users match docker/clickhouse/users.xml from the PostHog repo)
|
||||
CLICKHOUSE_HOST: clickhouse
|
||||
CLICKHOUSE_DATABASE: posthog
|
||||
CLICKHOUSE_SECURE: "false"
|
||||
CLICKHOUSE_VERIFY: "false"
|
||||
CLICKHOUSE_API_USER: api
|
||||
CLICKHOUSE_API_PASSWORD: apipass
|
||||
CLICKHOUSE_APP_USER: app
|
||||
CLICKHOUSE_APP_PASSWORD: apppass
|
||||
CLICKHOUSE_LOGS_CLUSTER_HOST: clickhouse
|
||||
CLICKHOUSE_LOGS_CLUSTER_SECURE: "false"
|
||||
# Redis / Kafka
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
FLAGS_REDIS_ENABLED: "false"
|
||||
# Object storage (MinIO)
|
||||
OBJECT_STORAGE_ENABLED: "true"
|
||||
OBJECT_STORAGE_ENDPOINT: http://objectstorage:19000
|
||||
OBJECT_STORAGE_PUBLIC_ENDPOINT: ${POSTHOG_SITE_URL}
|
||||
OBJECT_STORAGE_ACCESS_KEY_ID: object_storage_root_user
|
||||
OBJECT_STORAGE_SECRET_ACCESS_KEY: object_storage_root_password
|
||||
OBJECT_STORAGE_FORCE_PATH_STYLE: "true"
|
||||
# Session replay storage
|
||||
SESSION_RECORDING_V2_S3_ENDPOINT: http://objectstorage:19000
|
||||
SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: object_storage_root_user
|
||||
SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: object_storage_root_password
|
||||
RECORDING_API_URL: http://recording-api:6738
|
||||
# App
|
||||
SITE_URL: ${POSTHOG_SITE_URL}
|
||||
SECRET_KEY: ${POSTHOG_SECRET_KEY}
|
||||
ENCRYPTION_SALT_KEYS: ${POSTHOG_ENCRYPTION_SALT_KEYS}
|
||||
DEPLOYMENT: hobby
|
||||
IS_BEHIND_PROXY: "true"
|
||||
DISABLE_SECURE_SSL_REDIRECT: "true"
|
||||
# "false" so login works over plain HTTP; set to "true" once the domain uses HTTPS
|
||||
SECURE_COOKIES: ${POSTHOG_SECURE_COOKIES}
|
||||
CDP_API_URL: http://plugins:6738
|
||||
FEATURE_FLAGS_SERVICE_URL: http://feature-flags:3001
|
||||
LIVESTREAM_HOST: ${POSTHOG_SITE_URL}/livestream
|
||||
API_QUERIES_PER_TEAM: '{"1": 100}'
|
||||
OTEL_SDK_DISABLED: "true"
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ""
|
||||
|
||||
services:
|
||||
# One-shot: fetches the ClickHouse configuration, Kafka table schemas and
|
||||
# funnel UDFs from the PostHog repo (pinned to the same commit as the app
|
||||
# image) plus the GeoLite2 GeoIP database. Idempotent across redeploys.
|
||||
init-assets:
|
||||
image: alpine:3.21
|
||||
restart: on-failure
|
||||
logging: *default-logging
|
||||
entrypoint: /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
apk add --no-cache git curl brotli >/dev/null
|
||||
if [ ! -f /assets/.ok-${POSTHOG_VERSION} ]; then
|
||||
echo "Fetching PostHog repo assets @ ${POSTHOG_VERSION}"
|
||||
rm -rf /tmp/ph && mkdir -p /tmp/ph && cd /tmp/ph
|
||||
git init -q .
|
||||
git remote add origin https://github.com/PostHog/posthog.git
|
||||
git sparse-checkout set --no-cone /docker/clickhouse/ /posthog/idl/ /posthog/user_scripts/
|
||||
git -c protocol.version=2 fetch -q --depth 1 --filter=blob:none origin ${POSTHOG_VERSION}
|
||||
git checkout -q FETCH_HEAD
|
||||
rm -rf /assets/clickhouse /assets/idl /assets/user_scripts /assets/.ok-*
|
||||
cp -r docker/clickhouse /assets/clickhouse
|
||||
cp -r posthog/idl /assets/idl
|
||||
cp -r posthog/user_scripts /assets/user_scripts
|
||||
touch /assets/.ok-${POSTHOG_VERSION}
|
||||
echo "Repo assets ready"
|
||||
fi
|
||||
if [ ! -s /share/GeoLite2-City.mmdb ]; then
|
||||
echo "Downloading GeoLite2 database"
|
||||
curl -sL --http1.1 https://mmdbcdn.posthog.net/ | brotli --decompress --output=/share/GeoLite2-City.mmdb
|
||||
chmod 644 /share/GeoLite2-City.mmdb
|
||||
fi
|
||||
echo "init-assets done"
|
||||
volumes:
|
||||
- posthog-assets:/assets
|
||||
- geoip-data:/share
|
||||
|
||||
db:
|
||||
image: postgres:15.12-alpine
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
POSTGRES_USER: posthog
|
||||
POSTGRES_DB: posthog
|
||||
POSTGRES_PASSWORD: posthog
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U posthog"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 10s
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ../files/db-init:/docker-entrypoint-initdb.d:ro
|
||||
|
||||
redis7:
|
||||
image: redis:7.2-alpine
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
command: redis-server --maxmemory-policy allkeys-lru --maxmemory 200mb
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
|
||||
# Required by ClickHouse for distributed DDL (docker/clickhouse/config.xml
|
||||
# points at zookeeper:2181)
|
||||
zookeeper:
|
||||
image: zookeeper:3.7.0
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
ZOO_AUTOPURGE_PURGEINTERVAL: 1
|
||||
ZOO_AUTOPURGE_SNAPRETAINCOUNT: 3
|
||||
volumes:
|
||||
- zookeeper-data:/data
|
||||
- zookeeper-datalog:/datalog
|
||||
|
||||
# Kafka API provided by Redpanda (as upstream), tuned down for small servers
|
||||
kafka:
|
||||
image: docker.io/redpandadata/redpanda:v25.1.9
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
command:
|
||||
- redpanda
|
||||
- start
|
||||
- --kafka-addr internal://0.0.0.0:9092
|
||||
- --advertise-kafka-addr internal://kafka:9092
|
||||
- --rpc-addr kafka:33145
|
||||
- --advertise-rpc-addr kafka:33145
|
||||
- --mode dev-container
|
||||
- --smp 1
|
||||
- --memory 1G
|
||||
- --reserve-memory 0M
|
||||
- --overprovisioned
|
||||
- --set redpanda.empty_seed_starts_cluster=false
|
||||
- --seeds kafka:33145
|
||||
- --set redpanda.auto_create_topics_enabled=true
|
||||
volumes:
|
||||
- redpanda-data:/var/lib/redpanda/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:9644/v1/status/ready || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 20s
|
||||
|
||||
# One-shot: pre-creates the Kafka topics that consumers check at startup
|
||||
kafka-init:
|
||||
image: docker.io/redpandadata/redpanda:v25.1.9
|
||||
restart: on-failure
|
||||
logging: *default-logging
|
||||
entrypoint: /bin/sh
|
||||
depends_on:
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
TOPICS="clickhouse_events_json \
|
||||
clickhouse_ai_events_json \
|
||||
clickhouse_heatmap_events \
|
||||
clickhouse_ingestion_warnings \
|
||||
events_plugin_ingestion \
|
||||
events_plugin_ingestion_dlq \
|
||||
events_plugin_ingestion_overflow \
|
||||
events_plugin_ingestion_async \
|
||||
events_plugin_ingestion_historical \
|
||||
ingestion-clientwarnings-main-1 \
|
||||
heatmaps_ingestion \
|
||||
session_recording_snapshot_item_events \
|
||||
session_recording_snapshot_item_overflow \
|
||||
clickhouse_groups \
|
||||
clickhouse_person \
|
||||
clickhouse_person_distinct_id \
|
||||
clickhouse_app_metrics2 \
|
||||
log_entries \
|
||||
clickhouse_tophog"
|
||||
for topic in $$TOPICS; do
|
||||
rpk topic create "$$topic" --brokers kafka:9092 -p 1 -r 1 2>/dev/null \
|
||||
|| rpk topic list --brokers kafka:9092 | grep -q "$$topic"
|
||||
done
|
||||
echo "Kafka topics ready"
|
||||
|
||||
clickhouse:
|
||||
image: clickhouse/clickhouse-server:26.6.1.1193
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
init-assets:
|
||||
condition: service_completed_successfully
|
||||
zookeeper:
|
||||
condition: service_started
|
||||
kafka:
|
||||
condition: service_started
|
||||
entrypoint: /bin/bash
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
cp /assets/clickhouse/config.xml /etc/clickhouse-server/config.xml
|
||||
cp /assets/clickhouse/config.d/default.xml /etc/clickhouse-server/config.d/default.xml
|
||||
cp /assets/clickhouse/users.xml /etc/clickhouse-server/users.xml
|
||||
cp /assets/clickhouse/user_defined_function.xml /etc/clickhouse-server/user_defined_function.xml
|
||||
mkdir -p /var/lib/clickhouse/format_schemas /var/lib/clickhouse/user_scripts
|
||||
cp -r /assets/idl/. /var/lib/clickhouse/format_schemas/
|
||||
cp -r /assets/user_scripts/. /var/lib/clickhouse/user_scripts/
|
||||
chmod -R 755 /var/lib/clickhouse/user_scripts
|
||||
chown -R clickhouse:clickhouse /var/lib/clickhouse/format_schemas /var/lib/clickhouse/user_scripts || true
|
||||
# Materialize system log tables (system.crash_log is required by the
|
||||
# custom_metrics ClickHouse migration), mirroring upstream init-db.sh
|
||||
(
|
||||
i=0
|
||||
while [ $$i -lt 90 ]; do
|
||||
if clickhouse-client --query "SELECT 1" >/dev/null 2>&1; then
|
||||
clickhouse-client --query "SYSTEM FLUSH LOGS" && echo "system logs flushed" && break
|
||||
fi
|
||||
sleep 2
|
||||
i=$$((i+1))
|
||||
done
|
||||
) &
|
||||
exec /entrypoint.sh
|
||||
environment:
|
||||
CLICKHOUSE_SKIP_USER_SETUP: 1
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
volumes:
|
||||
- posthog-assets:/assets:ro
|
||||
- clickhouse-data:/var/lib/clickhouse
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
|
||||
# Django app: runs migrations, then serves the API + UI on :8000
|
||||
web:
|
||||
image: posthog/posthog:${POSTHOG_VERSION}
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
command: ["sh", "-c", "./bin/migrate && exec ./bin/docker-server"]
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
kafka-init:
|
||||
condition: service_completed_successfully
|
||||
objectstorage:
|
||||
condition: service_started
|
||||
environment:
|
||||
<<: *django-env
|
||||
NGINX_UNIT_APP_PROCESSES: 2
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fs http://localhost:8000/_health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
start_period: 600s
|
||||
|
||||
# Celery worker + scheduler
|
||||
worker:
|
||||
image: posthog/posthog:${POSTHOG_VERSION}
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
command: ./bin/docker-worker-celery --with-scheduler
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
web:
|
||||
condition: service_started
|
||||
environment:
|
||||
<<: *django-env
|
||||
POSTHOG_SKIP_MIGRATION_CHECKS: "1"
|
||||
WEB_CONCURRENCY: 2
|
||||
|
||||
# Node plugin server: CDP (destinations, webhooks, transformations)
|
||||
plugins:
|
||||
image: posthog/posthog-node:${POSTHOG_NODE_VERSION}
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
clickhouse:
|
||||
condition: service_started
|
||||
kafka-init:
|
||||
condition: service_completed_successfully
|
||||
init-assets:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
# Run only the pipelines this template ships (CDP destinations/workflows,
|
||||
# realtime cohorts, feature-flag evaluation). The default mode would also
|
||||
# start the logs/error-tracking ingestion consumers, which this template
|
||||
# intentionally omits.
|
||||
NODEJS_CAPABILITY_GROUPS: cdp_workflows,realtime_cohorts,feature_flags
|
||||
SITE_URL: ${POSTHOG_SITE_URL}
|
||||
SECRET_KEY: ${POSTHOG_SECRET_KEY}
|
||||
ENCRYPTION_SALT_KEYS: ${POSTHOG_ENCRYPTION_SALT_KEYS}
|
||||
DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
PERSONS_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
BEHAVIORAL_COHORTS_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
CYCLOTRON_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
CDP_REDIS_HOST: redis7
|
||||
CDP_REDIS_PORT: 6379
|
||||
# TLS for these defaults to "true" in production images; this stack's
|
||||
# Redis is plaintext (a TLS handshake against it hangs until timeout and
|
||||
# crash-loops the process if these consumers ever run).
|
||||
LOGS_REDIS_HOST: redis7
|
||||
LOGS_REDIS_TLS: "false"
|
||||
TRACES_REDIS_HOST: redis7
|
||||
TRACES_REDIS_TLS: "false"
|
||||
COOKIELESS_REDIS_HOST: redis7
|
||||
COOKIELESS_REDIS_PORT: 6379
|
||||
CLICKHOUSE_HOST: clickhouse
|
||||
CLICKHOUSE_DATABASE: posthog
|
||||
CLICKHOUSE_SECURE: "false"
|
||||
CLICKHOUSE_VERIFY: "false"
|
||||
OBJECT_STORAGE_ENABLED: "true"
|
||||
OBJECT_STORAGE_ENDPOINT: http://objectstorage:19000
|
||||
OBJECT_STORAGE_PUBLIC_ENDPOINT: ${POSTHOG_SITE_URL}
|
||||
OBJECT_STORAGE_ACCESS_KEY_ID: object_storage_root_user
|
||||
OBJECT_STORAGE_SECRET_ACCESS_KEY: object_storage_root_password
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ""
|
||||
volumes:
|
||||
- geoip-data:/share:ro
|
||||
|
||||
# Node plugin server: event ingestion (Kafka -> person processing -> ClickHouse)
|
||||
ingestion-general:
|
||||
image: posthog/posthog-node:${POSTHOG_NODE_VERSION}
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
clickhouse:
|
||||
condition: service_started
|
||||
kafka-init:
|
||||
condition: service_completed_successfully
|
||||
init-assets:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
PLUGIN_SERVER_MODE: ingestion-v2-combined
|
||||
ENCRYPTION_SALT_KEYS: ${POSTHOG_ENCRYPTION_SALT_KEYS}
|
||||
DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
PERSONS_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
BEHAVIORAL_COHORTS_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
COOKIELESS_REDIS_HOST: redis7
|
||||
COOKIELESS_REDIS_PORT: 6379
|
||||
CDP_REDIS_HOST: redis7
|
||||
LOGS_REDIS_HOST: redis7
|
||||
LOGS_REDIS_TLS: "false"
|
||||
TRACES_REDIS_HOST: redis7
|
||||
TRACES_REDIS_TLS: "false"
|
||||
CLICKHOUSE_HOST: clickhouse
|
||||
CLICKHOUSE_DATABASE: posthog
|
||||
CLICKHOUSE_SECURE: "false"
|
||||
CLICKHOUSE_VERIFY: "false"
|
||||
volumes:
|
||||
- geoip-data:/share:ro
|
||||
|
||||
# Node plugin server: session replay blob ingestion (Kafka -> S3)
|
||||
ingestion-sessionreplay:
|
||||
image: posthog/posthog-node:${POSTHOG_NODE_VERSION}
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
kafka-init:
|
||||
condition: service_completed_successfully
|
||||
objectstorage:
|
||||
condition: service_started
|
||||
environment:
|
||||
PLUGIN_SERVER_MODE: recordings-blob-ingestion-v2
|
||||
ENCRYPTION_SALT_KEYS: ${POSTHOG_ENCRYPTION_SALT_KEYS}
|
||||
DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
SESSION_RECORDING_V2_S3_ENDPOINT: http://objectstorage:19000
|
||||
SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: object_storage_root_user
|
||||
SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: object_storage_root_password
|
||||
SESSION_RECORDING_V2_S3_TIMEOUT_MS: 120000
|
||||
|
||||
# Node plugin server: session replay playback API
|
||||
recording-api:
|
||||
image: posthog/posthog-node:${POSTHOG_NODE_VERSION}
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
clickhouse:
|
||||
condition: service_started
|
||||
objectstorage:
|
||||
condition: service_started
|
||||
environment:
|
||||
PLUGIN_SERVER_MODE: recording-api
|
||||
ENCRYPTION_SALT_KEYS: ${POSTHOG_ENCRYPTION_SALT_KEYS}
|
||||
DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
SESSION_RECORDING_API_REDIS_HOST: redis7
|
||||
SESSION_RECORDING_API_REDIS_PORT: 6379
|
||||
CLICKHOUSE_HOST: clickhouse
|
||||
CLICKHOUSE_DATABASE: posthog
|
||||
CLICKHOUSE_SECURE: "false"
|
||||
CLICKHOUSE_VERIFY: "false"
|
||||
SESSION_RECORDING_V2_S3_ENDPOINT: http://objectstorage:19000
|
||||
SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: object_storage_root_user
|
||||
SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: object_storage_root_password
|
||||
|
||||
# Rust event capture endpoint (/e, /batch, /capture, /i/v0)
|
||||
capture:
|
||||
image: ghcr.io/posthog/posthog/capture:master
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
ADDRESS: 0.0.0.0:3000
|
||||
KAFKA_TOPIC: events_plugin_ingestion
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
CAPTURE_MODE: events
|
||||
RUST_LOG: info,rdkafka=warn
|
||||
CAPTURE_V1_SINKS: msk
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_HOSTS: kafka:9092
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_MAIN: events_plugin_ingestion
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_HISTORICAL: events_plugin_ingestion_historical
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_OVERFLOW: events_plugin_ingestion_overflow
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_DLQ: events_plugin_ingestion_dlq
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_EXCEPTION: ingestion-errortracking-main
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_HEATMAP: heatmaps_ingestion
|
||||
CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_CLIENT_INGESTION_WARNING: ingestion-clientwarnings-main-1
|
||||
|
||||
# Rust capture endpoint for session recordings (/s)
|
||||
replay-capture:
|
||||
image: ghcr.io/posthog/posthog/capture:master
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
ADDRESS: 0.0.0.0:3000
|
||||
KAFKA_TOPIC: session_recording_snapshot_item_events
|
||||
KAFKA_HOSTS: kafka:9092
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
CAPTURE_MODE: recordings
|
||||
|
||||
# Rust feature flag evaluation service (/flags)
|
||||
feature-flags:
|
||||
image: ghcr.io/posthog/posthog/feature-flags:master
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
init-assets:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
WRITE_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
READ_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
PERSONS_WRITE_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
PERSONS_READ_DATABASE_URL: postgres://posthog:posthog@db:5432/posthog
|
||||
MAXMIND_DB_PATH: /share/GeoLite2-City.mmdb
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
ADDRESS: 0.0.0.0:3001
|
||||
RUST_LOG: info
|
||||
COOKIELESS_REDIS_HOST: redis7
|
||||
COOKIELESS_REDIS_PORT: 6379
|
||||
volumes:
|
||||
- geoip-data:/share:ro
|
||||
|
||||
# Rust hypercache server (/array remote config + /surveys)
|
||||
hypercache-server:
|
||||
image: ghcr.io/posthog/posthog/hypercache-server:master
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
redis7:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
REDIS_URL: redis://redis7:6379/
|
||||
ADDRESS: 0.0.0.0:3002
|
||||
RUST_LOG: info
|
||||
|
||||
# Live events stream (/livestream)
|
||||
livestream:
|
||||
image: ghcr.io/posthog/posthog/livestream:master
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
LIVESTREAM_JWT_SECRET: ${POSTHOG_SECRET_KEY}
|
||||
volumes:
|
||||
- ../files/livestream/configs.yml:/configs/configs.yml:ro
|
||||
|
||||
objectstorage:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
MINIO_ROOT_USER: object_storage_root_user
|
||||
MINIO_ROOT_PASSWORD: object_storage_root_password
|
||||
entrypoint: sh
|
||||
command: -c 'mkdir -p /data/posthog && minio server --address ":19000" --console-address ":19001" /data'
|
||||
volumes:
|
||||
- objectstorage-data:/data
|
||||
|
||||
# Caddy routes the single public domain to the right internal service,
|
||||
# exactly like the upstream hobby deployment (TLS is terminated by Traefik)
|
||||
proxy:
|
||||
image: caddy:2.10-alpine
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
depends_on:
|
||||
web:
|
||||
condition: service_started
|
||||
entrypoint: sh
|
||||
command: -c 'printf "%s" "$$CADDYFILE" > /etc/caddy/Caddyfile && exec caddy run -c /etc/caddy/Caddyfile'
|
||||
environment:
|
||||
CADDYFILE: |
|
||||
{
|
||||
auto_https off
|
||||
}
|
||||
:80 {
|
||||
@capture path /e /e/ /e/* /i/v0 /i/v0/ /i/v0/* /i/v1/analytics/events /i/v1/analytics/events/ /batch /batch/ /batch/* /capture /capture/ /capture/*
|
||||
@replay-capture path /s /s/ /s/*
|
||||
@flags path /flags /flags/ /flags/* /api/feature_flag/local_evaluation /api/feature_flag/local_evaluation/ /api/feature_flag/local_evaluation/*
|
||||
@surveys path /surveys /surveys/ /api/surveys /api/surveys/
|
||||
@remote-config path /array/*
|
||||
@webhooks path /public/webhooks /public/webhooks/ /public/webhooks/* /public/m/ /public/m/*
|
||||
@livestream path /livestream /livestream/ /livestream/*
|
||||
@objectstorage path /posthog /posthog/ /posthog/*
|
||||
|
||||
handle @capture {
|
||||
reverse_proxy capture:3000
|
||||
}
|
||||
handle @replay-capture {
|
||||
reverse_proxy replay-capture:3000
|
||||
}
|
||||
handle @flags {
|
||||
reverse_proxy feature-flags:3001
|
||||
}
|
||||
handle @surveys {
|
||||
reverse_proxy hypercache-server:3002
|
||||
}
|
||||
handle @remote-config {
|
||||
reverse_proxy hypercache-server:3002
|
||||
}
|
||||
handle @webhooks {
|
||||
reverse_proxy plugins:6738
|
||||
}
|
||||
handle @livestream {
|
||||
uri strip_prefix /livestream
|
||||
reverse_proxy livestream:8080 {
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
handle @objectstorage {
|
||||
reverse_proxy objectstorage:19000
|
||||
}
|
||||
handle {
|
||||
reverse_proxy web:8000
|
||||
}
|
||||
}
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
redis-data:
|
||||
zookeeper-data:
|
||||
zookeeper-datalog:
|
||||
redpanda-data:
|
||||
clickhouse-data:
|
||||
posthog-assets:
|
||||
geoip-data:
|
||||
objectstorage-data:
|
||||
27
blueprints/posthog/instructions.md
Normal file
27
blueprints/posthog/instructions.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# PostHog
|
||||
|
||||
Self-hosted PostHog based on the official "hobby" deployment, trimmed to run on a single server.
|
||||
|
||||
## Requirements
|
||||
|
||||
- **RAM: at least 8 GB free** is strongly recommended (the stack runs Django, a Celery worker, two Node plugin-server processes, ClickHouse, Redpanda/Kafka, Postgres, Redis, MinIO and several small Rust services).
|
||||
- ~15 GB of free disk for the images and data volumes.
|
||||
|
||||
## First boot
|
||||
|
||||
The first deployment takes **5-10 minutes**: an init container fetches the ClickHouse configuration and the GeoIP database, and the `web` service runs all Postgres and ClickHouse migrations before it starts serving. Watch the `web` service logs; once you see the Unit server start, open the domain and create the first account.
|
||||
|
||||
## After enabling HTTPS
|
||||
|
||||
If you serve PostHog over HTTPS (recommended for production), update these environment variables and redeploy:
|
||||
|
||||
- `POSTHOG_SITE_URL` to `https://<your-domain>` (otherwise logins/CSRF and SDK snippets point at the wrong scheme)
|
||||
- `POSTHOG_SECURE_COOKIES` to `true` (marks session/CSRF cookies as Secure; the default `false` is what allows logging in over the initial plain-HTTP domain)
|
||||
|
||||
## What is not included
|
||||
|
||||
Compared to the full upstream hobby stack, this template omits Temporal + Elasticsearch (data warehouse / batch exports), browserless (dashboard image exports and heatmap screenshots), and the error-tracking/logs/traces ingestion pipelines. Product analytics, feature flags, experiments, surveys, session replay, live events and the CDP destinations pipeline all work.
|
||||
|
||||
## Versioning
|
||||
|
||||
PostHog no longer publishes tagged releases; images are pinned to a tested master commit via the `POSTHOG_VERSION` and `POSTHOG_NODE_VERSION` environment variables. To upgrade, set `POSTHOG_VERSION` to a newer commit sha of `posthog/posthog` (Docker Hub tag) and redeploy.
|
||||
19
blueprints/posthog/meta.json
Normal file
19
blueprints/posthog/meta.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "posthog",
|
||||
"name": "PostHog",
|
||||
"version": "master-2026-07-14",
|
||||
"description": "PostHog is an open-source product analytics platform with feature flags, session replay, surveys, A/B testing and a customer data pipeline, all in one tool.",
|
||||
"logo": "posthog.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/PostHog/posthog",
|
||||
"website": "https://posthog.com/",
|
||||
"docs": "https://posthog.com/docs/self-host"
|
||||
},
|
||||
"tags": [
|
||||
"analytics",
|
||||
"product-analytics",
|
||||
"feature-flags",
|
||||
"session-replay",
|
||||
"ab-testing"
|
||||
]
|
||||
}
|
||||
10
blueprints/posthog/posthog.svg
Normal file
10
blueprints/posthog/posthog.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="142px" viewBox="0 0 256 142" version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
|
||||
<title>PostHog</title>
|
||||
<g>
|
||||
<path d="M0,112.012238 C0,107.259346 5.74647061,104.878898 9.10731886,108.239907 L33.5593478,132.692042 C36.9201961,136.053051 34.5399086,141.799308 29.7870161,141.799308 L5.33492848,141.799308 C2.38852989,141.799308 0,139.410858 0,136.464374 L0,112.012238 Z M0,86.2562448 C0,87.6710693 0.562072622,89.0282764 1.56256477,90.0285765 L51.7707846,140.236706 C52.7712447,141.237006 54.1282385,141.799308 55.5430629,141.799308 L83.1204028,141.799308 C87.8732953,141.799308 90.2537428,136.053051 86.8927345,132.692042 L9.10731886,54.9065733 C5.74647061,51.545565 0,53.9260124 0,58.678905 L0,86.2562448 Z M0,32.9228582 C0,34.3377359 0.562072622,35.6947297 1.56256477,36.6951899 L105.104065,140.236706 C106.104365,141.237006 107.461572,141.799308 108.876396,141.799308 L136.453736,141.799308 C141.206629,141.799308 143.587076,136.053051 140.226068,132.692042 L9.10731886,1.57329328 C5.74652396,-1.78750162 0,0.592732447 0,5.34562498 L0,32.9228582 Z M53.3333333,32.9228582 C53.3333333,34.3377359 53.8956354,35.6947297 54.8959354,36.6951899 L150.892734,132.692042 C154.253743,136.053051 160,133.672603 160,128.919711 L160,101.342371 C160,99.9275462 159.437698,98.5703391 158.437398,97.570039 L62.4405988,1.57323993 C59.0795905,-1.78755497 53.3333333,0.592732447 53.3333333,5.34562498 L53.3333333,32.9228582 Z M115.773932,1.57329328 C112.412924,-1.78755497 106.666667,0.592732447 106.666667,5.34562498 L106.666667,32.9228582 C106.666667,34.3377359 107.228969,35.6947297 108.229269,36.6951899 L150.892734,79.3587089 C154.253743,82.7197172 160,80.3392697 160,75.5863772 L160,48.0090373 C160,46.5942129 159.437698,45.2370057 158.437398,44.2367056 L115.773932,1.57329328 Z" fill="#F9BD2B"></path>
|
||||
<path d="M226.866993,112.666301 L176.650862,62.4496362 C173.289854,59.0886279 167.543596,61.4690753 167.543596,66.2219679 L167.543596,136.46384 C167.543596,139.410324 169.932046,141.798774 172.87853,141.798774 L250.665066,141.798774 C253.61155,141.798774 256,139.410324 256,136.46384 L256,130.067255 C256,127.120771 253.601414,124.768065 250.679471,124.387684 C241.713047,123.219867 233.325998,119.124772 226.866993,112.666301 Z M193.143276,124.732321 C188.433063,124.732321 184.61005,120.909307 184.61005,116.199094 C184.61005,111.488348 188.433063,107.665334 193.143276,107.665334 C197.854023,107.665334 201.677036,111.488348 201.677036,116.199094 C201.677036,120.909307 197.854023,124.732321 193.143276,124.732321 Z" fill="#000000"></path>
|
||||
<path d="M8.65608667,107.833264 L9.10731886,108.239373 L33.5594012,132.691509 C36.7740745,135.906386 34.7360374,141.303609 30.3932265,141.766855 L29.7870161,141.798774 L5.33493381,141.798774 C2.59898732,141.798774 0.344064852,139.739346 0.0358919824,137.086021 L0,136.46384 L0,112.011705 C0,107.46546 5.2576404,105.089845 8.65608667,107.833264 Z M8.65608667,54.5009977 L9.10731886,54.9071068 L53.3333333,99.1331746 L53.3333333,141.799841 L1.56256477,90.02911 C0.728821313,89.195082 0.199535861,88.1139429 0.0462669171,86.9579665 L0,86.2567783 L0,58.6794385 C0,54.1331934 5.2576404,51.7575787 8.65608667,54.5009977 Z M0,5.34562498 C0,0.799379948 5.2576404,-1.57608838 8.65608667,1.16720334 L9.10731886,1.57329328 L53.3333333,45.7993078 L53.3333333,88.4659744 L1.56256477,36.6951899 C0.728821313,35.8614731 0.199535861,34.7801635 0.0462669171,33.6240907 L0,32.9228582 L0,5.34562498 Z" fill="#1D4AFF"></path>
|
||||
<path d="M53.3333867,45.7998412 L105.104065,97.5705725 C105.938093,98.4041559 106.467214,99.4855914 106.620421,100.641679 L106.666667,101.342904 L106.666667,141.799841 L54.8959354,90.02911 C54.062352,89.195082 53.532971,88.1139429 53.3796658,86.9579665 L53.3333867,86.2567783 L53.3333867,45.7998412 Z M53.3333867,99.1321076 L86.8927345,132.691509 C90.107612,135.906386 88.0694373,141.303609 83.7266141,141.766855 L83.1204028,141.798774 L53.3333867,141.798774 L53.3333867,99.1321076 Z M61.9894118,1.16720334 L62.4405988,1.57329328 L105.104065,44.2367056 C105.938093,45.070289 106.467214,46.1517246 106.620421,47.3078122 L106.666667,48.0090373 L106.666667,88.4659744 L54.8959354,36.6951899 C54.062352,35.8614731 53.532971,34.7801635 53.3796658,33.6240907 L53.3333867,32.9228582 L53.3333867,5.34562498 C53.3333867,0.799379948 58.5912711,-1.57608838 61.9894118,1.16720334 Z" fill="#F54E00"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
73
blueprints/posthog/template.toml
Normal file
73
blueprints/posthog/template.toml
Normal file
@@ -0,0 +1,73 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
secret_key = "${password:64}"
|
||||
salt_keys = "${hash:32}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "proxy"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
|
||||
[config.env]
|
||||
# PostHog stopped publishing tagged releases; images are pinned to a tested
|
||||
# master commit. POSTHOG_VERSION is both the posthog/posthog image tag and the
|
||||
# git ref used to fetch the matching ClickHouse configuration.
|
||||
POSTHOG_VERSION = "1e7b36ebe0da38a738fed250d175809a899ef422"
|
||||
POSTHOG_NODE_VERSION = "sha-82ea668"
|
||||
# Set to https://<your-domain> after enabling HTTPS on the domain
|
||||
POSTHOG_SITE_URL = "http://${main_domain}"
|
||||
# Set to "true" once the domain uses HTTPS (marks session/CSRF cookies Secure)
|
||||
POSTHOG_SECURE_COOKIES = "false"
|
||||
POSTHOG_SECRET_KEY = "${secret_key}"
|
||||
POSTHOG_ENCRYPTION_SALT_KEYS = "${salt_keys}"
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "/db-init/create-extra-dbs.sh"
|
||||
content = """
|
||||
#!/bin/bash
|
||||
# Creates the auxiliary databases PostHog services can point at
|
||||
# (mirrors docker/postgres-init-scripts in the PostHog repo)
|
||||
set -e
|
||||
set -u
|
||||
|
||||
for db in cyclotron posthog_persons behavioral_cohorts; do
|
||||
DB_EXISTS=$(psql -U "$POSTGRES_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='$db'")
|
||||
if [ -z "$DB_EXISTS" ]; then
|
||||
echo "Creating database '$db'..."
|
||||
psql -U "$POSTGRES_USER" -c "CREATE DATABASE $db;"
|
||||
psql -U "$POSTGRES_USER" -c "GRANT ALL PRIVILEGES ON DATABASE $db TO $POSTGRES_USER;"
|
||||
fi
|
||||
done
|
||||
"""
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "/livestream/configs.yml"
|
||||
content = """
|
||||
debug: false
|
||||
kafka:
|
||||
brokers: 'kafka:9092'
|
||||
topic: 'events_plugin_ingestion'
|
||||
group_id: 'livestream'
|
||||
security_protocol: 'PLAINTEXT'
|
||||
session_recording_enabled: true
|
||||
session_recording_security_protocol: 'PLAINTEXT'
|
||||
consumers:
|
||||
event:
|
||||
enabled: true
|
||||
brokers: 'kafka:9092'
|
||||
topic: 'events_plugin_ingestion'
|
||||
security_protocol: 'PLAINTEXT'
|
||||
group_id: 'livestream'
|
||||
session_recording:
|
||||
enabled: true
|
||||
brokers: 'kafka:9092'
|
||||
topic: 'session_recording_snapshot_item_events'
|
||||
security_protocol: 'PLAINTEXT'
|
||||
group_id: 'livestream-session-recordings'
|
||||
notification:
|
||||
enabled: false
|
||||
mmdb:
|
||||
path: 'GeoLite2-City.mmdb'
|
||||
"""
|
||||
Reference in New Issue
Block a user