# OpenAnalytics: cookieless web analytics you self-host, with revenue attribution and an MCP server

> OpenLabs-so/openanalytics is an AGPL-3.0 analytics stack in one pnpm monorepo: a tracker, a collector, a ClickHouse-backed query gateway and a Next.js dashboard. It installs as a pull of ten prebuilt images, and its upgrade path is a snapshot-and-restore, not a migration down.

**OpenLabs-so/openanalytics** — Open-source, privacy-first and cookieless web analytics with revenue attribution and an MCP server.

- Repository: https://github.com/OpenLabs-so/openanalytics
- Website: https://getopen.so
- Stars: 593 · Forks: 49
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/openlabs-so-openanalytics

## What OpenAnalytics solves, and who is meant to run it

Most hosted analytics products answer questions by keeping a cross-site profile of a person. OpenAnalytics takes the other route: one lightweight tracker script, no cookies, no cross-site profiles, and reads that the README describes as aggregate-only. That single design decision cascades through the whole product. Visitor identity is a salted hash that rotates every night, so a visitor trail is as long as a visit and never as long as a person. Real-time presence comes from a cache rather than a table scan, and the visitor names on that screen are generated per visitor and mean nothing outside the day they were minted. Geo lookup, when a site opts in, runs against a database on your own disk and never leaves the host.

The audience is narrower than "anyone who wants analytics". This is for teams that have a Linux host with Docker, roughly 4 GB of RAM and 25 GB of free disk, and that want the data to stay there. The feature list is aimed at a product team rather than a marketing department: page views, custom and attribute-driven events, sessions, web vitals, funnels, per-site retention, embeddable widgets, public share links, CSV and JSON import and export, a CLI, and revenue analytics from your own Stripe account. There is also an MCP server, which matters if you want an assistant to query the same data through a defined interface rather than a screen.

One thing to note before you commit: the repository is a monorepo of seven apps and a packages directory, not a single service. Postgres holds the control plane, ClickHouse holds events and rollups, and two Valkey instances split a durable event queue from a losable realtime cache. That is the real cost of adoption, more than the licence.

## The architecture: a tracker, a collector, and one signed door into ClickHouse

The data flow is stated plainly in the repository table. The tracker in `apps/tracker` is a few KiB of browser snippet with a byte budget that CI enforces. The collector in `apps/collector` validates, sanitizes, rate-limits and enqueues. The worker in `apps/worker` drains that queue into ClickHouse and handles sessions, rollups, exports, mail and deletions. The API in `apps/api` is the control plane: auth, sites, keys, sharing, event definitions, funnels, widgets, revenue, an AI assistant and MCP. The realtime app in `apps/realtime` is the SSE stream behind the live dashboard, and `apps/web` is the Next.js dashboard itself.

The interesting part is the read path. ClickHouse is reachable only through `apps/query-gateway`, which verifies Ed25519-signed query envelopes minted by the API. So a compromised frontend cannot talk to the event store directly, and neither can anything else that has not been handed a signed envelope. The README frames this as a boundary rather than a convention, and the same applies to the env files: each service refuses to start when it is handed a secret it has no business holding, which the template describes as the reason a leaked collector cannot reach ClickHouse.

Some rules here are enforced by CI jobs rather than by review. `apps/web` may import only `packages/contracts`, so the OpenAPI document is the single seam between frontend and backend. The Postgres schema this repository builds contains no billing tables, and a CI job asserts that against a real database rather than a file list. Those checks are worth knowing about because they constrain what you can change in a fork without fighting the build.

## Installing OpenAnalytics: a pull, not a build

A release publishes ten images to `ghcr.io/openlabs-so/openanalytics`, so a fresh host needs no toolchain. Before you start, point four names at the host: `app.example.com`, `api.example.com`, `c.example.com` and `rt.example.com`. Certificates are issued on the first boot, and issuance fails without those records, half an hour later and nowhere near the cause. The README gives this sequence:

```bash
git clone https://github.com/OpenLabs-so/openanalytics
cd openanalytics
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"   # newest release, not main
cd infra/selfhost
./generate-secrets.sh --domain example.com --email you@example.com --with-geoip
docker compose pull && docker compose up -d
```

The checkout is where the version is chosen, and it is chosen once. The generator reads the tag back out of the tree it is standing in, points `.env` at that release's images, and prints which it picked and why. On a branch, on `main`, or with no git at all it writes the build defaults instead. The reason is not convenience: the compose file, the env templates and the migrations ship with the images, so a release's images against another tree is a configuration nobody has tested.

When the stack is up, open `https://app.example.com` and create the first account. If you are on arm64, or you want to run a branch, the images are amd64 only, so you build the ten yourself with the same compose file and one flag; the README estimates about ten minutes and swap on a 4 GB box. Running from source is documented separately, and one ordering detail is easy to get wrong: the migration runners are compiled output, so `pnpm run build` comes before `pnpm run migrate:postgres`.

## Upgrades move forward only, and rollback is a restore

This is the part to read twice. Later, `./upgrade.sh` moves between releases and takes the snapshot that `./rollback.sh` needs, because migrations do not go down. The way back is a restore, and a restore discards everything that arrived after the upgrade. The script tells you that before it starts, not at rollback time when you no longer have a choice. RELEASING.md is where a version number here is defined, which is worth reading if you plan to pin.

For an analytics product, that is an acceptable trade. Losing a few hours of event data is annoying, not existential, and the alternative (writing and testing down-migrations for every schema change) is a permanent tax. What it does mean is that an upgrade is a decision with a data-loss window attached, and the window is as long as the time between the snapshot and the restore. If your team treats analytics as a system of record for revenue reporting, that window is a real business risk, and you should schedule upgrades accordingly rather than treating them like a routine pull.

The other operational constraint is the host itself. Four DNS records, about 4 GB of RAM and 25 GB of free disk are the stated requirements, and you are running Postgres, ClickHouse and two Valkey instances on that box. The README does not document resource behaviour under heavy ingest, so sizing beyond the stated minimum is something you would have to establish yourself.

## Where OpenAnalytics is the wrong choice

If you want a single binary you can drop on a VPS and forget, this is not that. The minimum viable deployment is four services plus three data stores, and the env template is explicit that a half-configured deployment fails at startup rather than at a customer request. That is a deliberate choice, and it is the right one for a system holding event data, but it makes the first hour harder than a one-line install.

The second limitation is the schema policy described above. Teams that run frequent schema changes and expect reversible migrations will find the snapshot-and-restore model awkward, and the README does not document any down-migration path. If your release process assumes `migrate down` as a safety net, budget for a different one.

Third, the arm64 story is a build, not a pull. The README states the images are amd64, and building the ten takes about ten minutes with swap on a 4 GB box. On a small ARM instance that is a real constraint, not a footnote.

Finally, consider whether you need the revenue and MCP surface at all. If you only want page views and referrers, you are paying for a ClickHouse cluster and a signed query gateway to answer questions a much smaller tool would answer. The architecture earns its keep when you are correlating traffic with Stripe revenue or letting an assistant query events through MCP; without those, it is overhead.

## How it compares with Plausible and GA4

The repository's own topics list Plausible and Google Analytics as the alternatives it positions against, so the comparison is fair to make. Plausible is also open source, cookieless and self-hostable, and it is the closer of the two. The practical difference is scope: Plausible is a focused web analytics product, while OpenAnalytics ships revenue attribution from your own Stripe account, CSV and JSON import and export, an MCP server and a CLI in the same repository. If you need those, the comparison is not close. If you do not, Plausible's smaller surface is a feature.

GA4 is the opposite trade. It is free to use at the tier most sites sit in, and it is hosted, so there is no host to size and no upgrade script to run. What you give up is the property the README leads with: no cookies, no cross-site profiles, aggregate-only reads, and geo lookup that runs against a database on your own disk rather than leaving the host. For teams whose analytics decision is driven by a data-processing agreement rather than by features, that is the whole argument.

The honest summary is that OpenAnalytics is not competing on ease of setup. It is competing on where the data lives, what it refuses to collect, and how much of the product surface (revenue, funnels, retention, MCP) you get once it is running.

## Conclusion

Adopt OpenAnalytics if you want cookieless, aggregate-only analytics on hardware you control, need revenue attribution from your own Stripe account, and are willing to run Postgres, ClickHouse and two Valkey instances. Skip it if you want a single binary, if you are on arm64 without ten minutes for a local build, or if you need schema migrations that reverse. Verify first that four DNS names resolve to the host before the first boot, that the release tag you checked out is the one generate-secrets.sh reported, and that you have a restore rehearsal for rollback.sh, because the only way back from an upgrade is a snapshot restore.

## FAQ

### What is the best free analytics tool?

That depends on what you are optimising for. OpenAnalytics is free to self-host under AGPL-3.0 and includes revenue attribution from your own Stripe account, funnels, retention and an MCP server, but it requires a Linux host with Docker, roughly 4 GB of RAM and 25 GB of disk. GA4 is hosted and free at the tier most sites sit in, and Plausible is also open source and self-hostable.

### Is Plausible free to use?

The README does not state Plausible's pricing. It only lists Plausible as an alternative that OpenAnalytics positions against, alongside Google Analytics, in the repository topics.

### Is GA4 free or paid?

The README does not give GA4's pricing. It describes GA4 only as the hosted alternative that OpenAnalytics is contrasted with, where the trade is that GA4 is hosted while OpenAnalytics keeps data on your own disk.

### What is the best analytics platform?

The README does not rank platforms. It describes OpenAnalytics as cookieless, privacy-first and self-hostable under AGPL-3.0, with Postgres for the control plane, ClickHouse for events and rollups, and two Valkey instances for the event queue and realtime cache.

### What is OpenAnalytics?

OpenAnalytics is an open-source, privacy-first and cookieless web analytics product from OpenLabs-so, licensed AGPL-3.0. It ships as a pnpm monorepo with a tracker, collector, worker, API, query gateway, realtime SSE stream, Next.js dashboard and a CLI called oa, and it supports revenue attribution and an MCP server.

## Sources

- [License: AGPL-3.0](https://github.com/OpenLabs-so/openanalytics/blob/main/LICENSE)
- [OpenLabs-so/openanalytics on GitHub](https://github.com/OpenLabs-so/openanalytics)
- [Project website](https://getopen.so)
- [README](https://github.com/OpenLabs-so/openanalytics/blob/main/README.md)
- [Releases](https://github.com/OpenLabs-so/openanalytics/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/openlabs-so-openanalytics
