The Problem
Product teams need a single, selfâhosted stack that captures analytics, feature flags, error tracking and LLM observability without wiring together dozens of SaaS tools. Managing separate services creates latency, data silos and operational overhead.
What This Does
The repo is a portfolio of 16 independent projects that together implement PostHogâs platform. The two largest components are:
posthog/â the core backend (â4.5âŻk code files) built on Django/Python, exposing REST/GraphQL APIs, handling event ingestion, HogQL parsing (common/hogql_parser/), and background workers.frontend/â the React/TypeScript UI (â4.2âŻk code files) that renders dashboards, session replays and flag controls.
Supporting projects include:
| Project | Code files | Notable purpose |
|---|---|---|
products/ | 3.1âŻk | Individual product modules (e.g., analytics, experiments). |
nodejs/ | 1.0âŻk | Nodeâbased services such as the recording rasterizer. |
rust/ | 714 | Highâperformance ingestion workers and the âhobbyâinstallerâ. |
ee/ | 848 | Enterpriseâonly extensions (feature flags, SSO). |
services/ | 402 | Auxiliary microâservices (e.g., ingestion pipeline). |
cli/ | 69 | Commandâline tooling (cli/src/main.rs). |
packages/ | 119 | Shared NPM packages (e.g., hogql_parser). |
playwright/ | 79 | Endâtoâend test harness. |
| ⌠| ⌠| Remaining folders contain docs, CI helpers, and tooling. |
Common techniques across the portfolio are Dockerâbased deployment, Goâbased installers, Rust workers, and a TypeScript monorepo for the UI. The codebase reuses the HogQL parser (both Python and TS) and shares schema definitions through common/.
How It Is Wired
Execution starts with the Dockerfile at the repository root. It builds a multiâstage image that:
- Installs the Go hobby installer â entry point
bin/hobby-installer/main.go. This binary boots the selfâhosted environment, writes runtime config, and then execs the backend server. - Runs the Django app â the containerâs CMD (see
posthog/Dockerfile) invokespython manage.py runserver(orgunicornin production). All HTTP traffic enters the Django URL router (posthog/api/urls.py), which forwards to view functions inposthog/api/views.py. - Spawns Rust ingestion workers â the backend launches the compiled binary from
rust/ingestion/viasubprocess.Popen. Workers read from the ClickHouse queue, parse events withcommon/hogql_parser, and write normalized data back to ClickHouse. - Serves the UI â a separate Node image built from
frontend/Dockerfile.noderunsnpm start, serving the compiled React bundle (frontend/src/). API calls from the UI hit the same Django endpoints, completing the full requestâresponse loop.
The HogQL parser is a hub: common/hogql_parser/package.json (TS) and common/hogql_parser/pyproject.toml (Python) compile to a shared grammar used by both the backend and UI. Changes here ripple through every product module that executes queries.
How To Use It
# Clone the repository
git clone https://github.com/moses-y/posthog
cd posthog
# Build the Docker image (uses the root Dockerfile)
docker build -t posthog:local .
# Run the container (example: expose ports 8000/3000)
docker run -p 8000:8000 -p 3000:3000 posthog:local
Configuration â the container expects an environment file named .env. A template exists at .env.example; copy it and fill in required keys (e.g., POSTHOG_DB_URL, CLICKHOUSE_URL).
Running the UI locally â after the container is up, the React dev server can be started with:
cd frontend
npm install
npm run dev # serves on http://localhost:3000
The CLI tools are built from Rust sources:
cd cli
cargo build --release
./target/release/posthog-cli --help
RealâWorld Use
A SaaS product can drop this Docker image behind its internal network, point its client libraries at http://<host>:8000/api/, and immediately gain event capture, feature flag rollout, and session replay without external subscriptions. The same image can be extended with the ee/ modules for SSO or advanced permissions.
Code Health & Issues
- High â
rust/.envis tracked despite.gitignore; remove withgit rm --cached rust/.envand rotate credentials. - High â Committed
.envfiles (rust/.env,rust/personhog-replica/.env) expose live secrets; delete, add to.gitignore, and replace with an empty.env.example. - Medium â Base images in
Dockerfileare not pinned by digest; switch tonode:24.13.0-bookworm-slim@sha256:<digest>. - Medium â No dependencyâvulnerability scan in CI; add
dependency-review-actionorosv-scannerto GitHub Actions. - Medium â Large static assets (
rust/cymbal/tests/static/*.js.map,historical.csv) should be moved to Git LFS or external storage. - Medium â Checkout step in
.github/workflows/build-hogql-parser-npm.ymlkeeps the token; setpersist-credentials: false. - Medium â
package.jsoncontains apostinstallscript; disable scripts in CI (npm ci --ignore-scripts) or migrate work to explicit build steps.
Additional observations: tests are abundant (â4.4âŻk files) and CI is present, but the repository still contains committed secrets and unpinned Docker bases, which pose immediate security risk.
The Bottom Line
PostHogâs codebase is a wellâstructured collection of backend, frontend and supporting services that can be selfâhosted with a single Docker image. It offers extensive analytics capabilities outâofâtheâbox, but the current commit includes serious secretâleakage and Docker hygiene issues that must be remedied before production use. Engineers comfortable with Django, React and container deployment will find it a solid foundation; teams lacking security review processes should address the highâseverity findings first.