Analytics cookies only (Google Analytics, PostHog). No ad networks, no cross-site tracking. See our privacy policy.
An all-in-one product analytics suite: event and funnel analytics, session replay, feature flags, A/B experiments, surveys, error tracking and a SQL data warehouse behind one SDK. The self-hosted 'hobby' deploy puts the whole platform on one server, about 36 containers including ClickHouse, PostgreSQL, Redpanda (Kafka-compatible), Redis, Elasticsearch, Temporal and Caddy; PostHog's docs ask for 4 vCPU, 16 GB of RAM and more than 30 GB of storage. Self-hosting is free-plan features only, with no support and no tagged releases. The code is MIT, except the ee/ directory, which is under PostHog's proprietary enterprise licence.
Runs on a VPS from $119.59/mo (Hetzner Cloud CPX52, 24 GB RAM) · prices checked Sep 2026
Pick self-hosted PostHog when you want its whole product suite (event analytics, session replay, feature flags, experiments and surveys behind one SDK) and your events must stay on your own server. It is a platform, not a page-view counter: the hobby deploy runs about 36 containers and idled at 16.8 GB of RAM in our install check. PostHog itself steers most people to its Cloud free tier. If you only need a Google Analytics replacement, Plausible or Umami do that in a fraction of the resources; if you want funnels and replays on lighter hardware, look at OpenPanel.
Follow the PostHog deploy guide →Paid link — we earn a commission if you shop through it.
Run these commands on your server:
# PostHog 'hobby' deploy — PostHog's official single-server installer, pinned to commit d945c98 (PostHog publishes no release tags), with HTTPS from its bundled Caddy, per our PostHog deploy guide. Needs Docker installed first, a domain pointing at the server and ports 80/443 open. PostHog says 4 vCPU and 16 GB of RAM; the stack uses nearly all of it, so 24 GB or more is comfortable.
# 16 GB swap as a safety net: there is no memory headroom on a 16 GB server
sudo fallocate -l 16G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# the installer works in the current directory and names the Compose project after it, so give it a directory of its own
mkdir -p ~/posthog-hobby && cd ~/posthog-hobby
DOMAIN=posthog.example.com
git clone --filter=blob:none https://github.com/PostHog/posthog.git && git -C posthog checkout d945c980fbd024bcb75daa029893b152b92986ef
# the compose files float 11 images on :master or :latest; pin each to the digest we installed (deploy-hobby reuses this checkout)
sed -i -e 's|image: caddy$|image: caddy@sha256:f2a1290d0463aad60660d4ec134943f183ee2a5f6c3eb7bf32dd984f2f020772|' -e 's|-node:${POSTHOG_NODE_TAG:-latest}|-node@sha256:863e76e040ad41aef79f605c33ec71f1529bddf94cfd88941c935da9bde97235|' -e 's|posthog/cymbal:master|posthog/cymbal@sha256:637ba14bc877adaf42b4cbbadca56eabe6e079124b707ba49a11cdf492b6eb78|' posthog/docker-compose.base.yml posthog/docker-compose.hobby.yml
sed -i -e 's|posthog/capture:master|posthog/capture@sha256:ba4c368efd4c0a000eb3d9969532cfa27d2da868c80821e2c06ab6b2a82b3128|' -e 's|posthog/capture-logs:master|posthog/capture-logs@sha256:45af6d72e28f064194f4453a08b90dd164e57398dbb22531f2a9ecf70d7e0c1d|' -e 's|posthog/property-defs-rs:master|posthog/property-defs-rs@sha256:50293c7208aff270f708c1d6f4b38913b1db0a01032009d9dde57ee6ecadf868|' posthog/docker-compose.base.yml posthog/docker-compose.hobby.yml
sed -i -e 's|posthog/livestream:master|posthog/livestream@sha256:2a903c1a9696ca8a86b776e19aba4e117cb42f4b69a88421a4b640d3f62be1a8|' -e 's|posthog/personhog-replica:master|posthog/personhog-replica@sha256:981a55b25dba403c9ce07b1f0e2d758ae416d8f9ab8f2f4c89dae793d6815341|' -e 's|posthog/personhog-router:master|posthog/personhog-router@sha256:bebc4e90bf9a2ebc05804d5a0f48b127a20ccdea1d4696c52b5cc5e5d0769e27|' posthog/docker-compose.base.yml posthog/docker-compose.hobby.yml
sed -i -e 's|posthog/feature-flags:master|posthog/feature-flags@sha256:a703b29359a10b10dd41d0150dad02a5e2cb04006b2dce23eb0d0be7d6451b2c|' -e 's|posthog/hypercache-server:master|posthog/hypercache-server@sha256:7f1d33d7f064a9e1a723528a25a95d9eb020111be6f94d6ee5e3e0909c5c40a0|' posthog/docker-compose.base.yml posthog/docker-compose.hobby.yml
# SKIP_HEALTH_CHECK: the installer gives up after 10 minutes, but first-boot migrations take about 56 minutes. Pulling the images takes about 17 more. It also sends your domain to PostHog as an install-start event.
curl -fsSL -o deploy-hobby https://raw.githubusercontent.com/PostHog/posthog/d945c980fbd024bcb75daa029893b152b92986ef/bin/deploy-hobby
SKIP_HEALTH_CHECK=1 bash deploy-hobby d945c980fbd024bcb75daa029893b152b92986ef "$DOMAIN"
# Caddy answers 502 until the migrations finish (about an hour on 4 vCPU); do not restart containers meanwhile. Then open https://posthog.example.com and create the first account — whoever opens the site first creates it.
timeout 150m bash -c 'until [ "$(curl -s -o /dev/null -w "%{http_code}" localhost/_health)" = 200 ]; do sleep 60; done'PostHog's own self-hosting docs are frank about the trade-offs, and our install check added a few more: