Self-hosted, privacy-focused web analytics for developers and small teams
Website · Docs · Live Demo · Docker Hub
- Privacy-focused — no cookies, no fingerprinting, GDPR-friendly by design
- Lightweight tracker — ~1.5 KB gzipped, zero dependencies
- Real-time dashboard — interactive charts powered by Recharts
- Funnels & journeys — built-in conversion funnels and visitor path visualization
- Multiple database support — PostgreSQL, ClickHouse, or SQLite for analytics storage
- Built on PayloadCMS 3 + Next.js 16 — modern, extensible full-stack architecture
- Docker image on Docker Hub —
docker pull tidemeter/tidemeterand you're running - SPA support — automatic history API interception for single-page apps
- Custom event tracking — track signups, clicks, purchases, anything
- Team collaboration — multi-user with role-based access
- Public dashboards — share read-only analytics views without requiring login
- REST API — query your analytics data programmatically
Pull the pre-built image from Docker Hub — no cloning, no build step:
# Start a PostgreSQL database (skip if you already have one)
docker run -d \
--name tidemeter-db \
-p 5432:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=tidemeter \
postgres:16-alpine
# Pull and run TideMeter
docker run -d \
-p 3700:3700 \
-e DATABASE_URL="postgresql://postgres:postgres@host.docker.internal:5432/tidemeter" \
-e PAYLOAD_SECRET="your-secret-key-minimum-32-characters" \
-e NEXT_PUBLIC_APP_URL="http://localhost:3700" \
tidemeter/tidemeter:latestBrowse all available tags on Docker Hub → tidemeter/tidemeter.
Clone the repo and bring up everything (app + database) with one command:
git clone https://github.com/tidemeter/tidemeter.git
cd tidemeter
cp .env.example .env
# Edit .env — at minimum, set PAYLOAD_SECRET and SESSION_SALT_SECRET
docker compose -f docker/docker-compose.yml up -dVisit http://localhost:3700 to create your admin account and add your first website.
Full documentation: tidemeter.com/docs
For contributors and anyone who wants to build from source.
- Node.js 22+
- pnpm 9+
- PostgreSQL 16+ (or use Docker for the database)
git clone https://github.com/tidemeter/tidemeter.git
cd tidemeter
pnpm install
cp .env.example .env
# Edit .env — point DATABASE_URL to your PostgreSQL instance
pnpm dev # Start dev server with Turbopack HMR
pnpm build # Production build
pnpm test # Run tests
pnpm lint # LintThe dev server starts at http://localhost:3700.
Add the tracking script to any website you want to monitor. The data-website-id comes from your TideMeter dashboard after adding a site.
<script
defer
data-website-id="YOUR_WEBSITE_ID"
src="https://your-tidemeter-domain.com/t.js"
></script>| Attribute | Description | Default |
|---|---|---|
data-website-id |
(required) Website ID from your dashboard | — |
data-host-url |
Override the analytics endpoint URL | Script origin |
data-auto-track |
Auto-track pageviews | true |
data-respect-dnt |
Respect the Do-Not-Track header | true |
data-domains |
Comma-separated list of allowed domains | All domains |
// Track a named event
tidemeter.track("signup", { plan: "pro" });
// Track a pageview manually (when auto-track is disabled)
tidemeter.track();TideMeter is a Turborepo monorepo with a clear separation between the application layer and the analytics data layer.
┌─────────────────────────────────────────────┐
│ apps/web │
│ Next.js 16 + PayloadCMS 3 │
│ (dashboard, admin, API routes) │
├──────────────┬──────────────┬───────────────┤
│ @tidemeter/ │ @tidemeter/ │ @tidemeter/ │
│ tracker │ analytics │ ui │
│ (t.js) │ (Drizzle ORM)│ (components) │
└──────────────┴──────┬───────┴───────────────┘
│
┌───────────┼───────────┐
│ │ │
PostgreSQL ClickHouse SQLite
- Dual database design — PayloadCMS manages application data (users, sites, settings) in PostgreSQL; analytics events are stored in a separate database that can be PostgreSQL, ClickHouse, or SQLite.
- Repository pattern — the
@tidemeter/analyticspackage defines anAnalyticsRepositoryinterface with swappable adapters (postgres,clickhouse), selected at runtime via theANALYTICS_DB_TYPEenv var. - Tracker —
@tidemeter/trackercompiles to a singlet.jsfile via Rollup, served as a static asset.
All configuration is done through environment variables. Copy .env.example to .env and adjust:
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string for PayloadCMS | postgresql://tidemeter:tidemeter@localhost:5432/tidemeter |
PAYLOAD_SECRET |
Secret for PayloadCMS auth (min 32 chars) | — |
ANALYTICS_DB_TYPE |
Analytics storage engine: postgresql, clickhouse, sqlite |
postgresql |
ANALYTICS_DATABASE_URL |
Connection string for analytics DB (PostgreSQL) | Same as DATABASE_URL |
CLICKHOUSE_URL |
ClickHouse HTTP endpoint | http://localhost:8123 |
CLICKHOUSE_DATABASE |
ClickHouse database name | tidemeter_analytics |
ANALYTICS_SQLITE_PATH |
Path to SQLite file (when using SQLite) | ./data/analytics.db |
NEXT_PUBLIC_APP_URL |
Public URL of the application | http://localhost:3700 |
SESSION_SALT_SECRET |
Secret for hashing visitor IDs (rotated daily) | — |
GEOIP_DB_PATH |
Path to MaxMind GeoLite2-City.mmdb (optional) | — |
See .env.example for the full annotated reference.
For high-traffic sites, use ClickHouse as the analytics storage engine. The override compose file adds a ClickHouse container and reconfigures the app:
docker compose -f docker/docker-compose.yml -f docker/docker-compose.ch.yml up -dThis starts PostgreSQL (for PayloadCMS) + ClickHouse (for analytics) + the TideMeter app.
TideMeter runs anywhere Docker runs. Minimum requirements: 1 CPU core, 1 GB RAM.
For production, place TideMeter behind a reverse proxy (Nginx, Caddy, Traefik) for HTTPS. See the Deployment Guide for Nginx/Caddy examples, Railway, Fly.io, and DigitalOcean instructions.
-
NODE_ENV=production -
PAYLOAD_SECRET— strong random value (32+ characters) -
SESSION_SALT_SECRET— strong random value -
NEXT_PUBLIC_APP_URL— your actual domain - HTTPS via reverse proxy
- Database backups scheduled
| Layer | Technology |
|---|---|
| Framework | Next.js 16 |
| CMS | PayloadCMS 3 |
| Styling | Tailwind CSS 4 |
| Charts | Recharts |
| State | React Hooks |
| Analytics ORM | Drizzle ORM |
| Build | Turborepo + pnpm |
| Runtime | Node.js 22 (Alpine) |
| Docker | Multi-stage Dockerfile + Compose |
tidemeter/
├── apps/
│ └── web/ # Next.js 16 + PayloadCMS application
├── packages/
│ ├── analytics/ # Analytics data layer (Drizzle ORM, adapters)
│ │ └── src/
│ │ ├── adapters/ # postgres, clickhouse implementations
│ │ ├── schema/ # Drizzle table definitions
│ │ ├── types.ts # Core interfaces (AnalyticsRepository, etc.)
│ │ └── factory.ts # Adapter factory
│ ├── tracker/ # Lightweight tracking script (Rollup → t.js)
│ ├── ui/ # Shared UI components
│ └── tsconfig/ # Shared TypeScript configs
├── docker/
│ ├── Dockerfile # Multi-stage production build
│ ├── docker-compose.yml # Default stack (PostgreSQL)
│ ├── docker-compose.ch.yml # ClickHouse override
│ └── clickhouse/ # ClickHouse init scripts
├── turbo.json # Turborepo pipeline config
├── pnpm-workspace.yaml # pnpm workspace definition
└── package.json # Root scripts (dev, build, test, lint)
MIT © 2026 TideMeter