What PostHog does that SaaS alternatives cannot
Mixpanel, Amplitude and Heap are cloud tools: your data goes to their servers, their retention policy dictates how long you can keep it, and sampling kicks in automatically once your volume exceeds certain thresholds. Self-hosted PostHog removes all three of those constraints. Events stay in your ClickHouse database, on your disk or your object storage — no copy leaves your infrastructure. retention has no imposed ceiling: you decide when to purge old data. There is no sampling: every event is recorded, even during traffic spikes. Add to that GDPR compliance without negotiating a DPA with a third party, the ability to modify the source code, and direct access to raw data in ClickHouse for your own SQL analyses.
Quick comparison: PostHog self-hosted vs Mixpanel Cloud
Scroll the table
| Criterion | PostHog self-hosted | Mixpanel Cloud |
|---|---|---|
| Monthly cost (1M events/month) | ~$15–20 (VPS cost) | ~$25–100+ |
| Data hosted on your servers | Yes | No |
| Simplified GDPR compliance | Yes (no third-party DPA) | Partial (DPA required) |
| Automatic sampling | None | Activated beyond tiers |
| Session recordings included | Yes | Paid option |
| Feature flags & A/B testing | Yes, included | Paid (Growth+ plan) |
| Raw SQL access to data | Yes (direct ClickHouse) | No |
Server requirements
PostHog ships ClickHouse, Kafka, Redis and PostgreSQL in the same Docker Compose stack. It is the most resource-intensive analytics solution in this category, and under-sizing the VPS causes ClickHouse crashes or Kafka timeouts from the very first events. Plan for at least 4 vCPU and 8 GB of RAM for light usage (less than one million events per month). For a more sustained load — 1 to 5 million events per month — aim for 8 vCPU and 16 GB of RAM. On the storage side, ClickHouse is very efficient at compression, but session recordings accumulate quickly: allow at least 50 GB of SSD, and add external object storage (S3 or MinIO) as soon as you enable recordings. On the software side: Docker Engine 24+ and Docker Compose v2, a domain name with an A record pointing to the VPS, and ports 80 and 443 open in your firewall.
Install PostHog with Docker Compose
Install Docker on the VPS
Connect via SSH, then install Docker with the official script:
curl -fsSL https://get.docker.com | sh. Add your user to the docker group to avoid sudo:usermod -aG docker $USER. Verify withdocker compose versionthat Compose v2 is available.Clone the PostHog repository and prepare the environment
Clone the official repository:
git clone https://github.com/PostHog/posthog.git && cd posthog. Copy the sample environment file:cp .env.example .env. Open.envin an editor to configure the essential variables.Configure environment variables
In
.env, set at a minimum:SECRET_KEY(generate a 50-character random key withpython3 -c "import secrets; print(secrets.token_hex(25))"),SITE_URL=https://posthog.your-domain.com,DISABLE_SECURE_SSL=false(ortrueif testing over HTTP locally without a certificate). Leave thePOSTGRES_*,REDIS_*andCLICKHOUSE_*defaults for a simple deployment.Start the stack and wait for the migration
Launch all services in the background:
docker compose -f docker-compose.yml up -d. The first time, PostHog automatically runs database migrations — this takes 2 to 5 minutes depending on server performance. Follow progress withdocker compose logs -f webuntil you seePostHog server is now listening on port 8000.Access the interface and create your account
Navigate to
http://localhost:8000(or via your domain if DNS is already configured). The first-launch wizard guides you through creating the admin account, naming your organisation, and creating your first project. Note theProject API Keyshown — you will need it to wire up the SDK.
Configure an nginx reverse proxy for HTTPS
Install nginx and Certbot
On the host machine (outside Docker):
apt install -y nginx certbot python3-certbot-nginx. Make sure your domainposthog.your-domain.comalready points to the VPS IP before this step — Certbot needs to validate via HTTP.Create the nginx vhost
Create
/etc/nginx/sites-available/posthogwith:server { listen 80; server_name posthog.your-domain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }. Enable it:ln -s /etc/nginx/sites-available/posthog /etc/nginx/sites-enabled/.Issue the TLS certificate
Run
certbot --nginx -d posthog.your-domain.com. Certbot automatically updates the vhost to redirect HTTP to HTTPS and configures automatic renewal. Test renewal withcertbot renew --dry-run.Reload nginx and verify
Apply the configuration:
nginx -t && systemctl reload nginx. Openhttps://posthog.your-domain.comin a browser — you should see the PostHog interface with a valid certificate. UpdateSITE_URLin.envif not already done, then restart the web service:docker compose restart web.
Object storage for session recordings (S3 or MinIO)
Why object storage is necessary
Session recordings generate many small files (one per session segment). Storing them on the VPS local disk fills the SSD quickly and complicates backups. An S3-compatible object storage — whether AWS S3, Cloudflare R2 or self-hosted MinIO — externalises this storage elastically.
Deploy MinIO on the same VPS (self-hosted option)
If you do not want to depend on an external service, add MinIO to your
docker-compose.yml:minio: image: minio/minio environment: MINIO_ROOT_USER: posthog MINIO_ROOT_PASSWORD: change-me-strong-password command: server /data --console-address ':9001' volumes: - ./minio-data:/data. Then create a bucket namedposthogin the MinIO console (port 9001).Configure PostHog environment variables
In
.env, add:OBJECT_STORAGE_ENABLED=true,OBJECT_STORAGE_ENDPOINT=http://minio:9000(or your S3 bucket URL),OBJECT_STORAGE_ACCESS_KEY_ID=posthog,OBJECT_STORAGE_SECRET_ACCESS_KEY=change-me-strong-password,OBJECT_STORAGE_BUCKET=posthog. Restart the stack:docker compose up -d.Verify with a first session recording
In PostHog project settings, enable 'Session recording'. Browse your application for a few seconds, then return to PostHog under 'Session Recordings'. A recording should appear within a few minutes. Check in MinIO (or S3) that files have been created in the bucket.
Send your first events from your application
Install the JavaScript SDK
In your front-end project:
npm install posthog-js. If you use a framework such as React, Vue or Next.js, import and initialise PostHog once at the root level of your application.Initialise with your project key and your instance URL
Add the initialisation:
import posthog from 'posthog-js'; posthog.init('phc_YOUR_PROJECT_API_KEY', { api_host: 'https://posthog.your-domain.com', autocapture: true });. Replacephc_YOUR_PROJECT_API_KEYwith the key visible in PostHog project settings. Withautocapture: true, PostHog automatically records clicks, form submissions and page changes.Capture custom events
For a specific business event, call:
posthog.capture('order_placed', { amount: 49.90, currency: 'USD', product: 'vps-cloud-4vcpu' });. The properties associated with the event will be available in funnels, cohorts and ClickHouse SQL queries.Verify in the dashboard
Go to PostHog under 'Activity' or 'Events'. Events appear in near-real-time (a few seconds of latency). If nothing arrives after two minutes, check that
api_hostin the init exactly matches your instance URL, and that the TLS certificate is valid.
Enable Feature Flags
Feature flags let you roll out a feature progressively — first to 5% of users, then 20%, then 100% — without recompiling or redeploying your application. In PostHog, go to 'Feature Flags' and create a new flag with a key (my-new-module), a rollout condition (percentage or user cohort), and optionally variants for a proper A/B test. On the JavaScript side, evaluate the flag: if (posthog.isFeatureEnabled('my-new-module')) { /* show the new feature */ }. For server-side evaluation (Python or Node.js), use the corresponding backend SDK with the same PROJECT_API_KEY — evaluation then happens without a client round-trip, avoiding content flash. PostHog automatically records a $feature_flag_called event on every evaluation, letting you correlate a flag activation with any metric in your dashboard.
Performance tip: disable session recordings at launch
If your VPS is configured with the minimum 4 vCPU / 8 GB of RAM, session recordings consume significant ClickHouse resources from the very first hours of traffic. Recommendation: leave recordings disabled for the first 30 days. This gives you time to validate stack stability, fine-tune the object storage configuration, and add RAM if needed. Enable them afterwards in project settings — all future recordings will be captured, and you can set a sampling rate to record only a percentage of sessions and keep storage under control.
Updating PostHog
The PostHog team releases regular updates. To update: git pull origin master in the repository directory, then docker compose pull to fetch the new images, then docker compose up -d. Database migrations run automatically when the web service restarts — a brief interruption of a few tens of seconds is normal. Check the release notes before each major update: some versions require an intermediate update (no version skipping).
Official documentation
This guide covers the basic VPS installation. For advanced topics — ClickHouse replication, SAML SSO, webhooks, data warehouse integrations, and Kafka configuration options — refer to the official PostHog documentation. It remains the reference for major updates and specific use cases.