
GA4 に慣れていると、最初はそれで十分に感じます。
ページビューを追うだけなら、確かに十分です。
でも、プロダクトの成長とともに「もう少し踏み込んだことを知りたい」という場面が増えてきます。
GA4 では届かない場所がある――PostHog を選ぶ判断軸
現場でよく聞くのは、こういった詰まり方です。
カスタムイベントを細かく設計しようとすると、GA4 のイベント構造の制約が邪魔をする。
ユーザーの行動を時系列で追いたいのに、データ保持期間が最大 14 ヶ月で、それ以前のデータは消えてしまう。
そして最近増えているのが、「ユーザーデータを Google のサーバーに送ることへの懸念」です。
社内のセキュリティ審査や、顧客との契約上の制約で、サードパーティへのデータ送信が難しくなるケースが出てきています。
Mixpanel や Amplitude も同じ問題を抱えています。
どれも SaaS 専用で、自社インフラへのデプロイができません。
データは必ず彼らのクラウドに乗ります。
PostHog が他と決定的に違う点は、セルフホストできることです。
OSS として公開されており、自分たちのサーバーで完全に動かせます。
データが自社インフラから出ない、という要件を満たせるのは、現状 PostHog がほぼ唯一の現実的な選択肢です。
PostHog が持つ機能は、アナリティクスだけではありません。
セッションリプレイ、フィーチャーフラグ、A/B テスト、エラートラッキングが一つのプラットフォームに統合されています。
GA4 にはセッションリプレイもフィーチャーフラグも存在しないので、この差は大きいです。
クラウド版 PostHog には無料枠があり、小規模なら費用なしで始められます。
データ主権の要件がなければ、クラウド版から入るのが正直おすすめです。
セルフホストを選ぶのは、「データを社外に出せない」という制約が明確にある場合、あるいはデータ量が増えてクラウド版のコストが見合わなくなってきた場合です。
ここから先は、その セルフホスト構成 を実際に動かすところまで追います。
Docker Compose で PostHog を立ち上げる
PostHog の公式が示すセルフホストの最小スペックは、4 vCPU / 16GB RAM / 30GB 以上のストレージです。
ClickHouse と Kafka を同居させる構成上、どうしても必要なラインで、RAM が足りないとコンテナが OOM Kill されます。
環境を用意するときに、ここをケチらないことが大事です。
PostHog の Docker Compose 構成は、登場人物が多いです。
PostHog 本体(web と worker の 2 プロセス)、ClickHouse(イベントの列指向ストレージ)、Postgres(メタデータ管理)、Redis(キャッシュとキュー)、Kafka(イベントの取り込みバッファ)、MinIO(オブジェクトストレージ)、Caddy(リバースプロキシ兼 TLS 終端)が揃って初めて動きます。
公式リポジトリから compose ファイルを取得して起動するのが最も確実です。
# 公式のデプロイスクリプトで取得する場合
curl -fsSL https://raw.githubusercontent.com/posthog/posthog/HEAD/bin/deploy-hobby | bash
# または手動でリポジトリをクローンして使う
git clone https://github.com/PostHog/posthog.git
cd posthog
cp .env.example .env
.env に設定する最低限の環境変数はこちらです。
SECRET_KEY=ここに長いランダム文字列を入れる
SITE_URL=https://analytics.yourdomain.com
DATABASE_URL=postgres://posthog:パスワード@posthog-db:5432/posthog
REDIS_URL=redis://posthog-redis:6379/
CLICKHOUSE_HOST=posthog-clickhouse
KAFKA_HOSTS=posthog-kafka:9092
OBJECT_STORAGE_ENABLED=true
OBJECT_STORAGE_ENDPOINT=http://posthog-minio:19000
OBJECT_STORAGE_ACCESS_KEY_ID=object_storage_root_user
OBJECT_STORAGE_SECRET_ACCESS_KEY=任意のシークレット
SECRET_KEY は Django の署名キーなので、推測されない長さのランダム文字列を生成してください。
SITE_URL は後から変えると内部リンクが壊れることがあるので、最初から本番ドメインを入れておくのが無難です。
起動と確認の手順はシンプルです。
# 起動(初回はイメージの pull に時間がかかる)
docker compose up -d
# マイグレーション完了を待つ(5〜10 分程度)
docker compose logs -f posthog-web
# コンテナの状態確認
docker ps
ログに「Starting PostHog server」が流れてきたら、ブラウザで管理画面にアクセスできます。
初回アクセス時にアカウント作成画面が出るので、そこで管理者ユーザーを作ります。
起動しない場合の原因は大体 2 パターンです。
ポート競合(80 番や 443 番を他のプロセスが使っている)か、RAM 不足でコンテナが落とされているかです。
docker compose logs でサービス名を指定してエラーを確認し、どちらか切り分けてください。
永続化ボリュームの設定漏れ が、セルフホストで一番よくある痛い失敗です。
デフォルトの compose 設定にはボリューム定義が含まれていますが、カスタマイズ時に消してしまうと、コンテナを再起動するたびにデータが消えます。
ClickHouse と Postgres のボリュームが named volume として定義されているか、必ず確認してください。
SDK を繋いでイベントを送る――identify まで済ませておく理由
JavaScript SDK の導入は npm 経由で行います。
npm install posthog-js
初期化のコードです。
import posthog from 'posthog-js'
posthog.init('YOUR_PROJECT_API_KEY', {
api_host: 'https://analytics.yourdomain.com', // セルフホストの URL
defaults: '2026-01-30',
})
GA4 との最大の違いは、api_host に自ホストの URL を指定する点です。
ここを省略すると PostHog のクラウドにデータが飛んでしまうので、セルフホストの意味がなくなります。
プロジェクト API キーは管理画面の「Project Settings」から確認できます。
イベントの計測はこれだけです。
posthog.capture('button_clicked', {
button_name: 'signup',
page: '/top',
})
プロパティは自由に追加できます。
GA4 のようにイベントパラメータの種類や数に制限がないので、必要な情報をそのまま渡せます。
フィーチャーフラグを使うなら、identify はログイン直後に呼ぶのを鉄則にしてください。
// ログイン後などユーザーが判明したタイミングで呼ぶ
posthog.identify('user-123', {
email: 'taro@example.com',
plan: 'pro',
})
フラグの評価は distinct_id を軸に行われます。
identify を呼ぶ前は、PostHog が自動生成した匿名 ID でフラグが評価されます。
その状態でフラグを参照すると、ログイン済みユーザーに対して意図した割合でフラグが当たらないことがあります。
ログイン処理の直後に identify を呼ぶパターンを最初から習慣にしておくと、後で混乱しません。
フィーチャーフラグで新機能を段階リリースする
管理画面の「Feature Flags」から「New feature flag」を作成します。
フラグキーは英数字とハイフンで構成する識別子で、コードから参照するときに使います。
ここでは new-dashboard というキーで作ったとします。
ロールアウト設定で「Percentage of users」を選び、最初は 5% に設定します。
これで全ユーザーのうち 5% にだけフラグが有効になります。
保存したら、すぐにコードから参照できる状態になります。
フラグを参照する実装はこちらです。
import { usePostHog } from 'posthog-js/react'
import { useEffect, useState } from 'react'
export default function Dashboard() {
const posthog = usePostHog()
const [showNewDashboard, setShowNewDashboard] = useState(false)
useEffect(() => {
const flagValue = posthog.isFeatureEnabled('new-dashboard')
// フラグがまだ取得できていない場合は undefined が返る
if (typeof flagValue !== 'undefined') {
setShowNewDashboard(flagValue)
}
}, [])
return (
<>
{showNewDashboard ? <NewDashboard /> : <LegacyDashboard />}
</>
)
}
ここで注意が必要なのは、isFeatureEnabled の戻り値が undefined になるタイミングがあることです。
PostHog の初期化直後、フラグの取得リクエストがまだ完了していない間は undefined が返ります。
typeof で確認してから state を更新しないと、フラグが取れていない状態で古い UI を表示してしまいます。
フォールバックを false に固定したい場合は、isFeatureEnabled の戻り値に対して ?? false でデフォルト値を補うのが素直です。
5% で問題がなければ 50% に上げ、さらに問題がなければ 100% にする、というのが段階リリースの基本的な流れです。
管理画面でスライダーを動かすだけで即時反映されます。
ただし、ロールアウト割合を下げるときには注意 が必要です。
PostHog のフラグ評価はデフォルトで distinct_id のハッシュを使うため、同じユーザーには一貫して同じ結果が返ります(スティッキネス)。
50% から 10% に戻したとき、元々フラグが当たっていたユーザーの一部は引き続き当たり続けます。
「新機能を完全に非表示に戻したい」という場合は、割合を下げるのではなくフラグ自体を無効化するのが確実です。
本番に持ち込む前に確認しておくこと
動くことを確認したら、本番投入の前に押さえておきたい点がいくつかあります。
順番に整理します。
まず データの永続化とバックアップ です。
docker compose down してから docker compose up -d を実行し、Postgres と ClickHouse のデータが残っているかを必ず検証してください。
named volume が正しく定義されていれば消えませんが、カスタマイズ時に設定を崩してしまうケースが多いです。
バックアップは Postgres であれば pg_dump、ClickHouse は公式の clickhouse-backup ツールを使った定期実行を仕込んでおくのが最低限の保険になります。
次に HTTPS 化 です。
Caddy が組み込まれているため、SITE_URL にドメインを設定すれば自動で TLS 証明書を取得してくれます。
ただし、ポート 80/443 が外部から到達できる状態でないと証明書取得が失敗します。
外部ロードバランサーで TLS を終端する場合は、DISABLE_SECURE_SSL_REDIRECT と IS_BEHIND_PROXY の環境変数を調整してください。
もう一点、イメージタグの管理 も見落としがちです。
latest タグのまま運用すると、意図しないタイミングで破壊的変更を踏む可能性があります。
リリースタグを固定して、計画的にアップデートする運用にしてください。
スペックについては、イベント量が増えると ClickHouse の負荷が上がります。
最初は 4 vCPU / 16GB RAM でも、データ量に応じてスケールアップの計画を持っておくと安心です。
なお、PostHog 公式は Kubernetes での高可用構成のサポートをすでに終了しています。
スケールアップが必要になった段階では、PostHog Cloud の EU リージョンへの移行か、より大きなシングルノードへの乗り換えが現実的な選択肢になります。
セルフホストは「動かす」より「維持する」ほうが手間がかかります。
その手間を引き受けてでもデータを手元に置きたい、という判断が明確にある場合に選ぶものです。
その判断が固まっているなら、PostHog はその要件に応えられる数少ないツールのひとつです。
株式会社ホコサキは、山口県宇部を拠点に Web 制作・業務システム開発・AI 活用支援・DX 推進を手がけています。
PostHog のような OSS ツールを活用したプロダクト分析基盤の構築や、データ主権を意識したシステム設計の相談も受け付けています。
ご興味があれば お問い合わせページ からお気軽にどうぞ。

