Links anonymous browser sessions to authenticated user identities, enabling unified
user journey tracking across login boundaries. This solves the "logged-out anonymous
session → logged-in session" tracking gap, providing complete funnel visibility and
accurate visitor deduplication.
## Changes
- Client-side: Persistent visitor ID in localStorage (data-identity-stitching attribute)
- Server-side: identity_link table linking visitors to distinct IDs (authenticated users)
- Query updates: getWebsiteStats now deduplicates by resolved identity
- Graceful degradation: Works in Safari private browsing and when localStorage unavailable
## Implementation Details
Uses hybrid approach combining client-side persistence with server-side linking:
- Visitor ID generated once per browser, persists across sessions
- When user logs in, identify() creates identity link
- stats queries join through identity_link to deduplicate cross-device sessions
Both PostgreSQL and ClickHouse supported with appropriate query patterns:
- PostgreSQL: normalized schema, joins through session table
- ClickHouse: denormalized with ReplacingMergeTree for deduplication
## Edge Cases Handled
- Safari private browsing: localStorage throws, visitorId undefined, no link created
- localStorage cleared: new visitorId generated, creates new link
- Multiple tabs: same visitorId shared via localStorage
- Multiple devices: one visitor can link to multiple distinct_ids
- Multiple accounts: one distinct_id can link to multiple visitors
## Test Plan
- [ ] Enable feature on test website (default enabled)
- [ ] Anonymous pageview - confirm visitor_id in events table
- [ ] Call umami.identify('user1') - confirm identity_link created
- [ ] Stats show 1 visitor (deduplicated)
- [ ] Log out, browse anonymously, stats still show 1 visitor
- [ ] Test with data-identity-stitching="false" - no visitor_id collected
- [ ] Test in Safari private browsing - no errors, gracefully skips
- [ ] Test ClickHouse: verify identity_link table populated and FINAL keyword works
- [ ] Verify retroactive: historical anonymous session attributed correctly
|
||
|---|---|---|
| .github | ||
| .husky | ||
| cypress | ||
| db | ||
| docker | ||
| podman | ||
| prisma | ||
| public | ||
| scripts | ||
| src | ||
| .dockerignore | ||
| .eslintignore | ||
| .eslintrc.json | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| .stylelintrc.json | ||
| app.json | ||
| cypress.config.ts | ||
| docker-compose.yml | ||
| Dockerfile | ||
| jest.config.ts | ||
| LICENSE | ||
| netlify.toml | ||
| next-env.d.ts | ||
| next.config.ts | ||
| package.components.json | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| postcss.config.js | ||
| README.md | ||
| rollup.tracker.config.js | ||
| tsconfig.json | ||
| tsconfig.prisma.json | ||
| tsup.config.js | ||
Umami
Umami is a simple, fast, privacy-focused alternative to Google Analytics.
🚀 Getting Started
A detailed getting started guide can be found at umami.is/docs.
🛠 Installing from Source
Requirements
- A server with Node.js version 18.18 or newer
- A database. Umami supports PostgreSQL (minimum v12.14) databases.
Get the Source Code and Install Packages
git clone https://github.com/umami-software/umami.git
cd umami
pnpm install
Configure Umami
Create an .env file with the following:
DATABASE_URL=connection-url
The connection URL format:
postgresql://username:mypassword@localhost:5432/mydb
Build the Application
pnpm run build
The build step will create tables in your database if you are installing for the first time. It will also create a login user with username admin and password umami.
Start the Application
pnpm run start
By default, this will launch the application on http://localhost:3000. You will need to either proxy requests from your web server or change the port to serve the application directly.
🐳 Installing with Docker
To build the Umami container and start up a Postgres database, run:
docker compose up -d
Alternatively, to pull just the Umami Docker image with PostgreSQL support:
docker pull docker.umami.is/umami-software/umami:latest
🔄 Getting Updates
Warning
If you are updating from Umami V2, image "postgresql-latest" is deprecated. You must change it to "latest". e.g., rename
docker.umami.is/umami-software/umami:postgresql-latesttodocker.umami.is/umami-software/umami:latest.
To get the latest features, simply do a pull, install any new dependencies, and rebuild:
git pull
pnpm install
pnpm run build
To update the Docker image, simply pull the new images and rebuild:
docker compose pull
docker compose up --force-recreate -d