database · tutorial · nextjs

Delete PostHog: $0 Analytics That Respect Users

Event tracking with a cookie-free mode and no bills: SQLite tables, SQL queries, CSV exports. Privacy plus $0 forever. Inside.

A census enumerator in a fur-trimmed parka stands beside a dog sled and its team on a snowy plain in Alaska, around 1940
A census enumerator in a fur-trimmed parka stands beside a dog sled and its team on a snowy plain in Alaska, around 1940 · Photo: Unknown photographer, public domain, via Wikimedia Commons

The analytics bill outgrew the revenue

Month three: 40k monthly events sailed past the free tier. Options: $450/year for watching users, or a week migrating.

Worse: the cookie banner the setup required. "We value your privacy" beside 14 toggles — conversion poison installed voluntarily, bouncing EU visitors before they read a word. Measurement was costing measurements.

Nextjs selfhosted analytics ends both taxes: events in owned tables (queryable forever, $0 always), a cookie-free mode when you want no cookie at all, CSV exports when investors ask. Observability as infrastructure, not subscription.

Rented vs owned analytics

Axis PostHog/cloud Self-hosted tables
Monthly cost $0 → $40+ at scale $0 forever
Cookie set by default Yes (tracking cookies) One session cookie (first-party)
Data ownership Their cloud (export limits) Your SQLite (SQL anytime)
EU privacy posture DPA + banners + trust No third-party transfer at all
Funnels/retention UI Polished dashboards SQL queries + CSV
Event volume ceiling Tiered (pay past free) Disk space (vast)

Three things billing teaches:

  1. Free tiers are acquisition, not pricing. Generous limits convert you; growth converts you to paid — the meter starts exactly when leaving gets painful (dashboards, history, team habits). Price analytics at 10x current volume on day one, not at signup generosity. Tiers are funnels wearing pricing.
  2. Banners cost more than tools. EU bounce from consent walls (measured: double-digit abandonment on first visit) dwarfs any analytics subscription — first-party tracking with cookie-free mode is a much simpler posture (verify your counsel). Privacy respect converts; compliance theater repels.
  3. SQL beats dashboards for founder questions. "Which pricing page variant converted trial users from last month's launch?" — one SQL query, thirty seconds. Dashboard tools answer their questions beautifully and yours eventually. Owned rows plus SQL is the fastest analytics interface ever built for ad-hoc founder curiosity.

The feedback-board post built the ballot box. This one builds the eyes.

Nextjs selfhosted analytics template

Two tables, one tracking snippet, SQL forever — the BoringPush analytics shape.

Step 1 — Track pageviews + key events, first-party.

create table if not exists "events" (
  "id" text primary key, "userId" text,            -- null when anonymous (fine)
  "name" text not null,                             -- pageview | signup | checkout | ...
  "path" text, "referrer" text, "createdAt" timestamptz default now());
create index if not exists "events_name_time_idx" on "events"("name","createdAt");

Server-side tracking (route middleware logs pageviews, handlers log conversions) — no client script, adblock-proof by construction. One session cookie by default, none in cookie-free mode.

Anonymous pageviews plus authenticated user IDs on money events: enough signal for every solo decision, zero privacy theater. Events are facts; dashboards are views.

Step 2 — Answer founder questions in SQL.

-- conversion last 30 days, by referrer (the question dashboards dodge):
select "referrer", count(*) filter (where "name"='checkout') * 1.0
  / nullif(count(*) filter (where "name"='signup'),0) as conv
from "events" where "createdAt" > now() - interval '30 days' group by 1;
-- revenue per source: join orders (same DB! no export-merge dance)

Same-database joins (events × orders × users) answer revenue questions no siloed tool can touch without pipelines. Thirty seconds per question, infinite flexibility, CSV export for investors (COPY (...) TO STDOUT CSV). SQL literacy pays here fastest anywhere in the stack — one afternoon learning aggregates funds years of instant answers.

Step 3 — Retain sanely, export freely.

# retention cron (90 days raw, aggregates forever):
# - nightly: roll up daily counts per event → events_daily (tiny, permanent)
# - prune raw past 90d (privacy posture + disk hygiene in one motion)
# export: one-click CSV route (admin allowlist) for advisors, acquirers, curiosity

Aggregates-forever plus raw-90-days balances insight against weight.

That's the entire idea — eyes everywhere, bills nowhere.

Rented vs owned, year one

Year of analytics Cloud tool Self-hosted tables
Cash cost ~$0 → $450+ (tier crossed) $0
Cookie posture Third-party, banner likely First-party, cookie-free mode
Ad-hoc question time Minutes–hours (UI wrangling) ~30s (SQL)
Revenue joins Export-merge dance Same-DB query
Volume ceiling event Tier negotiation Disk upgrade ($2/mo)

Stated plainly: no polished funnels/retention UI (SQL replaces dashboards, not replicates them), no session replay (heatmaps need client scripts — skip or add tiny ones deliberately), and SQL literacy required (real skill, real afternoon). Priced against $450 plus banner bounce plus export dances: queries win for founder-shaped questions, dashboards win for team-shared metrics. Solos ask founder questions.

FAQ

Is nextjs selfhosted analytics GDPR-clean?

Largely yes in common interpretations (first-party, cookie-free mode available, no cross-site tracking, minimal retention) — but this blog isn't legal counsel: verify with qualified advice for your jurisdiction and data uses. The posture (nothing leaves your server, nothing identifies across sites) is the strongest starting position available. Simplicity here is legal strategy wearing engineering clothes.

How do funnels work without a funnel UI?

SQL sequences (users completing step1→step2→step3 via window functions or staged counts) answer funnel questions precisely, if less prettily. Save the five queries you actually run as views (activation, checkout, retention cohorts); dashboards emerge organically around real questions instead of hypothetical ones. Funnels you query beat funnels you admire.

What about bot traffic polluting pageviews?

Filter server-side (user-agent blocklist for known crawlers, plus behavioral heuristics for the ambitious) — first-party logging sees what third-party scripts miss, including the bots adblockers hide from cloud tools. Cleaner data as a side effect of ownership: filtered numbers typically run ~30% below raw, and every remaining row is human. Owned measurement measures better.

Can I migrate historical PostHog data in?

Export events (their export APIs, CSV/JSON), transform to the two-table shape, import with original timestamps preserved — an afternoon per 100k events, roughly. Worth it for year-over-year continuity (launch comparisons need history); skip it if young (fresh tables plus aggregates going forward). History migrates when it earns the afternoon; otherwise begin clean.

Do I lose A/B testing without a feature-flag service?

No — flags are a tiny table (key, variant, userId hash) plus the events to measure outcomes (same DB, same SQL). Homegrown flags lack fancy targeting UIs and cost $0 with full data ownership.

The features posts detail the pattern; analytics closes the loop (variant → conversion in one query). Flags plus events, both yours, total visibility.

When should analytics graduate to a real platform?

Team-shared metrics (ten people reading dashboards daily), product analytics as a discipline (dedicated analyst, experiment cadence), or compliance-certified pipelines (audited retention, access controls). Graduate on organizational need, never on event volume (SQLite handles millions). Solo curiosity runs on SQL; graduate when curiosity becomes a department.

Keep reading

+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)

The Agent-First Shipping Kit

Command from your phone.

Built so autonomous AI agents build what you want without breaking what's already paying you.

$199

Get BoringPush

One-time payment · Lifetime updates · Unlimited projects.

Keep reading