openanalytics is a free, open source web & product analytics project written in TypeScript and released under AGPL-3.0. It has 537 GitHub stars, 47 forks and 1 open issues, and was last pushed 19 hours ago. On this registry it ranks #20 of 25 tracked projects in Web & Product Analytics, with 5 head-to-head comparisons available. It gained 2 stars over the last 3 tracked days.

What is openanalytics?

OpenAnalytics is open-source, privacy-first, cookieless web analytics for teams that want aggregate-only traffic, funnel, revenue and realtime reporting on hardware they control — self-hosted under AGPL-3.0 or run on the authors' hosted instance at getopen.so.

What it is

OpenAnalytics is a TypeScript pnpm monorepo that ships a complete web analytics product across several apps: apps/tracker holds the browser snippet, apps/collector ingests and validates events, apps/worker drains the queue, apps/api is the control plane for auth, sites, keys, sharing, event definitions, funnels, widgets, revenue, the AI assistant and the MCP server, apps/query-gateway is the only process permitted to read ClickHouse, apps/realtime backs the live dashboard over SSE, apps/web is the Next.js dashboard, and apps/cli provides the oa command for site setup, stats and device-flow login. Data lives in Postgres for the control plane, ClickHouse for events and rollups, and two Valkey instances, one durable event queue and one losable realtime cache.

The problem it solves is cookie-based, cross-site profiling of the kind a general-purpose web analytics suite performs. It replaces that with one lightweight tracker script, no cookies, no cross-site profiles and aggregate-only reads, while still covering page views, custom and attribute-driven events, sessions, web vitals, funnels and per-site retention. Revenue attribution comes from your own Stripe account rather than a vendor's, and the Postgres schema the repository builds contains no billing tables, a claim a CI job asserts against a real database rather than a file list.

Key capabilities

  • A tracker snippet measured in a few KiB, with a hard byte budget that fails CI when a change exceeds it.
  • An ingest pipeline where apps/collector validates, sanitizes, rate-limits and enqueues events, and apps/worker drains them into ClickHouse for sessions, rollups, exports, mail and deletions.
  • Strict query isolation: ClickHouse is reachable only through apps/query-gateway, which verifies Ed25519-signed query envelopes minted by apps/api.
  • Revenue analytics sourced from your own Stripe account, alongside CSV and JSON import and export.
  • An MCP server plus the oa CLI for site setup, stats and device-flow login.
  • A live view backed by a presence cache rather than a table scan, plus session-by-session visitor journeys where the identity is a salted hash that rotates every night.
  • Embeddable widgets, public share links, funnels, per-site retention and web vitals, with city-level geolocation only when a site opts in and only against a database on your own disk.

Who uses it and how

  • Self-hosters running a Linux host with Docker, about 4 GB of RAM and 25 GB of free disk, pointing four DNS records at it before first boot.
  • Teams replacing a cookie-based analytics suite who need aggregate reporting and revenue attribution without cross-site profiles.
  • Operators who prefer not to run infrastructure at all, using the hosted instance at getopen.so, which is the same code on someone else's servers.
  • Privacy-constrained organizations that need the identity trail to last as long as a visit and never as long as a person.
  • Product teams wanting funnels, retention, widgets, share links and an MCP server reachable from the same dashboard.

Getting started

A release publishes ten images to ghcr.io/openlabs-so/openanalytics, so installing is a pull rather than a build. SELF-HOSTING.md documents a generator script, one docker compose up -d, automatic TLS and an explanation of every secret and failure mode.

How it compares

The facts provide no list of paid products this project replaces, so its placement comes from its own topics, which pitch it as a Google Analytics alternative and a Plausible alternative. Against those, the differentiators the repository states are the licence, AGPL-3.0 with the source in one monorepo, self-hosting on your own hardware under your own ClickHouse and Postgres, data ownership including revenue pulled from your own Stripe account, and a cost model of running your own host or using the authors' hosted instance.

When to use it — and when not to

A self-hoster must operate Docker on a Linux host, Postgres, ClickHouse, two Valkey instances, TLS certificates and mail, and must point four DNS records at the host before first boot because issuance fails without them, half an hour later and nowhere near the cause. It is a poor fit for anyone unwilling to run a database-backed stack or to own certificate and DNS setup. The hosted option solves that, but it means the authors hold the servers.

project readme (upstream, from github) — read inline

OpenAnalytics

Open-source, privacy-first web analytics. One lightweight tracker script, no cookies, no cross-site profiles, aggregate-only reads — self-hostable on your own hardware under AGPL-3.0.

A hosted instance runs at getopen.so, operated by the authors: the same code, someone else's servers.

The Overview screen: visitors, pageviews, bounce rate, average visit and
revenue across a day, with top pages, referrers and revenue
underneath

Watch the dashboard in motion, a tour of these same screens on YouTube.

What is in this repository

The product, as one pnpm monorepo:

App Role
apps/tracker The browser snippet — a few KiB, with a byte budget CI enforces
apps/collector Ingest: validates, sanitizes, rate-limits, enqueues
apps/worker Drains the queue into ClickHouse; sessions, rollups, exports, mail, deletions
apps/api Control plane: auth, sites, keys, sharing, event definitions, funnels, widgets, revenue, AI assistant, MCP
apps/query-gateway The only process allowed to read ClickHouse; verifies signed query envelopes
apps/realtime The SSE stream behind the live dashboard
apps/web The dashboard (Next.js)
apps/cli oa — site setup, stats, device-flow login
packages/* domain, postgres (+migrations), clickhouse (+migrations), redis, auth, contracts (OpenAPI), observability, integrations, migrations, testkit

Stores: Postgres (control plane), ClickHouse (events and rollups), Valkey ×2 (one durable event queue, one losable realtime cache).

What it does, in one list: page views, custom and attribute-driven events, sessions, web vitals, funnels, per-site retention, embeddable widgets, public share links, revenue analytics from your Stripe account, CSV/JSON import and export, an MCP server, and a CLI.

Who is on the site now, from a presence cache rather than a table scan. Names are generated per visitor and mean nothing outside the day they were minted.

The realtime screen: five visitors online with the page each is on, and
everyone seen in the last 24 hours below them

Where one visitor went, session by session. The identity behind a trail is a salted hash that rotates every night, so the trail is as long as a visit and never as long as a person.

One visitor's trail: two visits, one arrived direct and one from X, each
listing the pages opened inside it

Where they are, at city level and only when a site opts in. The lookup runs against a database on your own disk and never leaves the host.

A globe with visitors placed on it, one each in eastern Europe, north Africa,
the Middle East and South America

Architecture rules CI enforces, not conventions:

  • apps/web may import only packages/contracts — the OpenAPI document is the single seam between frontend and backend.
  • ClickHouse is reachable only through the query gateway, which verifies Ed25519-signed query envelopes minted by the api.
  • The tracker has a hard byte budget; a change that exceeds it fails CI.
  • 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.

Self-hosting

SELF-HOSTING.md is the guide: a generator script, one docker compose up -d, automatic TLS, and an explanation of every secret and every failure mode. Requirements are a Linux host with Docker, four DNS records, about 4 GB of RAM and 25 GB of free disk.

Installing is a pull, not a build. A release publishes ten images to ghcr.io/openlabs-so/openanalytics, so a fresh host is a few minutes and needs no toolchain on it.

Point four names at the host before you start. Certificates are issued on the first boot and issuance fails without them, half an hour later and nowhere near the cause:

app.example.com   api.example.com   c.example.com   rt.example.com
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 [email protected] --with-geoip
docker compose pull && docker compose up -d
# then open https://app.example.com and create the first account

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 and points .env at that release's images, printing which it picked and why; on a branch, on main, or with no git at all it writes the build defaults instead. That is not a 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.

Images are amd64. On arm64, or to run a branch, build the ten here instead: same compose file, one flag, about ten minutes and swap on a 4 GB box. Later, ./upgrade.sh moves between releases and takes the snapshot ./rollback.sh needs, because migrations do not go down: the way back is a restore, and a restore discards what arrived after the upgrade. It tells you that before it starts, not at rollback time when you no longer have a choice. RELEASING.md is what a version number here means.

To run it from source instead — for development, or to slot the services into infrastructure you already have — follow Running from source. The order matters and one obvious order does not work: the migration runners are compiled output, so pnpm run build comes before pnpm run migrate:postgres.

infra/selfhost/env/*.env.example documents every variable each service reads, and there is one file per service on purpose — the environment schema forbids some keys to some services, so a single shared .env cannot be correct. The AI assistant (OPENAI_API_KEY) and object storage are optional: unset, those surfaces disable themselves and everything else runs.

Run the test suite the way CI does:

pnpm run test          # unit + contract + tracker, no infrastructure needed
pnpm run verify        # everything CI checks, including boundaries and the size budget

Privacy model

No cookies, no fingerprinting, no cross-site identifiers. Visitor identity is a daily-rotating salted hash; raw IP addresses are never stored. Do Not Track and Global Privacy Control are honored at the collector, before anything is written. City-level geolocation is opt-in per site.

Geolocation is resolved locally against a database on your own disk — no lookup ever leaves the host. None is bundled (a 60 MB download, and stale within a month); infra/selfhost/geoip/fetch-dbip.sh downloads one.

IP Geolocation by DB-IP — https://db-ip.com — used under CC BY 4.0.

Contributing

Pull requests are merged here, with your name on the commit. This repository used to receive periodic exports from a private monorepo, and a merged PR was flattened by the next one; that ended in August 2026. A bot asks you to sign the CLA once — it is not an assignment, you keep your copyright — and there is no DCO sign-off on top of it.

CONTRIBUTING.md has the setup, the ground rules CI enforces, and where to start. Discussions are for questions and for ideas worth talking through first.

Security reports: SECURITY.md — please not a public issue.

License and trademark

Code: AGPL-3.0. If you run a modified OpenAnalytics as a network service, the AGPL requires you to offer your modified source to its users.

The "OpenAnalytics" na

readme truncated — read the full docs on github

Frequently asked questions

Is openanalytics free to use?

openanalytics is open source under the AGPL-3.0 licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does openanalytics do?

Open-source, privacy-first and cookieless web analytics with revenue attribution and an MCP server.

What is openanalytics written in?

openanalytics is primarily written in TypeScript. Its source is publicly available at https://github.com/OpenLabs-so/openanalytics, and it has 537 GitHub stars.