Umami Banner

Let me tell you about the moment I realized Google Analytics is spyware.

I was setting up a simple portfolio website for a friend. Just a single-page site with a contact form. Nothing fancy.

I added Google Analytics (because, you know, “you need analytics”). Then I ran the site through a privacy checker.

Result: The website drops 14 cookies. It sends data to 8 different Google domains. It tracks users across 97% of the internet (thanks to Google’s ad network). Oh, and it requires a Cookie banner that covers 40% of the screen on mobile because GDPR.

My friend’s reaction: “Can’t we just… not track people?”

That’s when I discovered Umami.

One Docker container. No cookies. No personal data collection. GDPR compliant out of the box. And it actually shows you the metrics that matter (page views, referrers, devices, countries) without the surveillance capitalism.


The Google Analytics Enshittification Story

If you’ve used Google Analytics for more than a year, you’ve seen the decline.

2013: The Golden Era (Universal Analytics)

Google Analytics was the analytics tool. Free. Powerful. No cookie banner required (because enforcement was lax). Life was good.

2016-2020: The “Free” Phase

Google “gave” us free analytics. In exchange, they collected everything:

  • Your visitors’ IP addresses
  • Their device fingerprints
  • Their browsing history (across sites using Google Analytics)
  • Their demographic data (age, gender, interests)
  • Their location (down to the city block)

All to “improve ad targeting.”

2022: Google Analytics 4 (GA4)

Universal Analytics was “sunset” (killed). Everyone had to migrate to GA4.

GA4 is…

  • No more “pageviews” as a default metric (you have to configure “events”)
  • Confusing UI (even Google’s own support team can’t navigate it)
  • Even more data collection (now tracks “enhanced conversions” —
    Oh, and it still requires a cookie banner in the EU. Because it still collects personal data.

2023-Present: The Privacy Backlash

GDPR enforcement is getting stricter. EU courts have ruled that Google Analytics is illegal (because data is transferred to the US, where privacy laws don’t exist).

Companies are scrambling to find alternatives. Enter Umami, Plausible, Simple Analytics, and the rest of the “privacy-first analytics” crowd.


What Is Umami, Really?

Umami is a privacy-first, open-source web analytics tool. It’s what Google Analytics should have been before the MBAs took over.

Key features:

  • No cookies — Uses a session-based approach that doesn’t store personal data
  • No personal data collection — Doesn’t track IP addresses, device fingerprints, or browsing history
  • GDPR/CCPA compliant — Out of the box, no cookie banner required
  • Self-hosted — Your data stays on your server (or use their cloud, your choice)
  • Lightweight — The tracking script is 2.4 KB (Google Analytics is 45 KB)
  • Real-time analytics — See page views as they happen
  • Simple UI — You can actually understand the dashboard in 30 seconds
  • Open source — MIT license. Fork it. Modify it. Self-host it.

The Name “Umami” (Yes, It’s the Japanese Word)

Umami (うま味) is the fifth taste (alongside sweet, sour, salty, bitter). It means “pleasant savory taste.”

The creator (Francis A](https://github.com/mikecao)) named it Umami because analytics should be savory — satisfying,
It’s a nice sentiment. Unlike Google Analytics, which is about as satisfying as a root canal.


Getting Started (It’s Stupidly Easy)

1
2
3
4
5
6
docker run -d \
--name umami \
-p 3000:3000 \
-e DATABASE_URL=postgresql://umami:password@postgres:5432/umami \
-e HASH_SALT=your_random_salt_string \
ghcr.io/umami-software/umami:postgresql-latest

That’s it. Umami is running on http://localhost:3000.

But wait, you need a database. Here’s a full docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
version: '3'
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://umami:umami123@postgres:5432/umami
- HASH_SALT=your_random_salt_string_change_this
depends_on:
- postgres
restart: always

postgres:
image: postgres:16
environment:
- POSTGRES_USER=umami
- POSTGRES_PASSWORD=umami123
- POSTGRES_DB=umami
volumes:
- postgres_data:/var/lib/postgresql/data
restart: always

volumes:
postgres_data:

Run docker-compose up -d, open http://localhost:3000, and log in with:

  • Username: admin
  • Password: umami

Change the default password immediately. (Yes, I learned this the hard way.)

Option 2: Vercel + PlanetScale (Serverless)

If you don’t want to manage a VPS, you can deploy Umami to Vercel (frontend) + PlanetScale (database):

  1. Fork the Umami GitHub repo
  2. Create a PlanetScale database (free tier)
  3. Connect Vercel to your forked repo
  4. Set environment variables (DATABASE_URL, HASH_SALT)
  5. Deploy

Total cost: $0/month (Vercel free tier + PlanetScale free tier).

Compare that to Google Analytics (free,

Option 3: Umami Cloud (For the “I Don’t Want to Self-Host” Folks)

Umami offers a cloud version at umami.is:

  • Starter: $9/month (10,000 events/month)
  • Pro: $29/month (100,000 events/month)
  • Enterprise: Custom

Is it worth it? If you value your time more than $9/month, yeah. Self-hosting takes ~1 hour to set up. Cloud takes 5 minutes.


Adding Umami to Your Website (It’s 3 Lines of Code)

Once Umami is running, add the tracking script to your website:

1
2
3
4
5
6
<script
async
defer
data-website-id="your-website-id"
src="https://analytics.yourdomain.com/umami.js"
></script>

That’s it. No, seriously. That’s the entire tracking script.

Compare this to Google Analytics, which requires:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
</script>
<!-- And don't forget the cookie banner -->
<!-- And the GDPR compliance script -->
<!-- And the consent management platform -->
<!-- And... -->

Umami’s script is 2.4 KB. Google’s is 45 KB (and that’s before you add the cookie banner library).

Framework Integrations (Because We All Use Next.js Now)

Umami has official integrations for popular frameworks:

Next.js (App Router):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }) {
return (
<html>
<head>
<Script
src="https://analytics.yourdomain.com/umami.js"
data-website-id="your-website-id"
strategy="afterInteractive"
/>
</head>
<body>{children}</body>
</html>
);
}

Nuxt 3:

1
2
3
4
5
6
7
8
9
10
11
12
13
// plugins/umami.ts
export default defineNuxtPlugin(() => {
useHead({
script: [
{
src: 'https://analytics.yourdomain.com/umami.js',
'data-website-id': 'your-website-id',
async: true,
defer: true,
},
],
});
});

Astro (what this blog uses!):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
// Layout.astro
---

<html>
<head>
<script
async
defer
data-website-id="your-website-id"
src="https://analytics.yourdomain.com/umami.js"
></script>
</head>
<body>
<slot />
</body>
</html>

The Dashboard (Finally, an Analytics UI You Can Understand)

Google Analytics 4’s dashboard looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Home
├── Reports
│ ├── Realtime
│ ├── Life cycle
│ │ ├── Acquisition
│ │ │ ├── User acquisition
│ │ │ ├── Traffic acquisition
│ │ ├── Engagement
│ │ │ ├── Events
│ │ │ ├── Conversions
│ │ │ ├── Pages and screens
│ │ ├── Monetization
│ │ │ ├── Ecommerce purchases
│ │ │ ├── In-app purchases
│ │ ├── Retention
│ ├── User
│ │ ├── Demographics
│ │ ├── Tech

It’s like the Mars Climate Orbiter —
Umami’s dashboard looks like this:

1
2
3
4
5
6
7
Dashboard
├── Overview (page views, visitors, bounce rate, avg. session duration)
├── Pages (top pages by views)
├── Referrers (where traffic is coming from)
├── Devices (browser, OS, device type)
├── Countries (where visitors are from)
├── Events (custom events you've defined)

That’s it. Six sections. You can understand your analytics in 30 seconds.

Screenshot Walkthrough (Because Words Are Hard)

1. Overview Page:

  • Total page views (today, yesterday, last 7 days, last 30 days)
  • Unique visitors
  • Bounce rate
  • Average session duration
  • Graph: Page views over time (line chart)

2. Pages Page:

  • Top pages by views (with percentages)
  • Dynamic filter by page path (e.g., /blog/*)

3. Referrers Page:

  • Top referrers (Google, Twitter, direct, etc.)
  • Gold mine: Shows you which marketing channels are actually working

4. Devices Page:

  • Browsers (Chrome, Firefox, Safari, etc.)
  • Operating systems (Windows, macOS, Linux, iOS, Android)
  • Device types (desktop, mobile, tablet)

5. Countries Page:

  • Top countries by visitors
  • Privacy note: Umami only stores country-level location (not city, not IP address)

Privacy Features (The Whole Point of Umami)

Here’s what Umami doesn’t collect:

  1. IP addresses — Umami anonymizes IPs before storing (only country is extracted)
  2. Device fingerprints — Doesn’t track unique device IDs
  3. Cross-site tracking — Umami only tracks your website, not your visitors across the internet
  4. Personal data — No names, emails, phone numbers, demographics
  5. Cookies — Uses sessionStorage (not cookies), which is not subject to GDPR cookie laws

Here’s what Umami does collect:

  1. Page views — Which pages were viewed
  2. Referrers — Where visitors came from (Google, Twitter, etc.)
  3. Browser/OS — Generic info (Chrome, Firefox, etc. — not specific versions)
  4. Country — Only country, not city/IP
  5. Device type — Desktop, mobile, tablet (generic categories)

That’s it. No personal data. No cookies. No cross-site tracking.

Is Umami Really GDPR Compliant?

Yes. Here’s why:

  1. No personal data — GDPR only applies to “personal data” (IP addresses, names, etc.). Umami doesn’t collect any.
  2. No cookies — The ePrivacy Directive (cookie law) only applies to cookies. Umami uses sessionStorage, which is not a cookie.
  3. No consent required — Since no personal data is collected and no cookies are used, you don’t need a cookie banner.

I’m not a lawyer (thank god),
Note: If you’re in Germany, you might still want a “we use analytics” note in your privacy policy (just to be safe).

Umami vs. The Competition (Let’s Be Thorough)

Umami vs. Google Analytics 4

Feature Google Analytics 4 Umami
Price Free (```) $0 (self-hosted) or $9/month (cloud)
Cookies required? Yes No
GDPR compliant? No (requires cookie banner) Yes
Data collection Extensive (personal data) Minimal (anonymous)
Script size 45 KB 2.4 KB
UI complexity Extreme Minimal
Self-hosted? No Yes
Data ownership Google owns your data You own your data

Verdict: Unless you need Google Ads integration (which requires GA), switch to Umami. Your visitors’ privacy will thank you.

Umami vs. Plausible

Plausible is another privacy-first analytics tool. It’s the main competitor to Umami.

Feature Plausible Umami
Price $9-59/month (cloud only) $0 (self-hosted) or $9/month (cloud)
Open source? No (source available,
Self-hosted? Yes (```) Yes
UI Polished Good
Custom events? Yes Yes
Real-time? Yes Yes

Verdict: Plausible is more “polished.” Umami is more “free if you self-host.” If you don’t mind paying $9/month, Plausible. If you want free + open source, Umami.

Umami vs. Simple Analytics

Simple Analytics is… simple. Almost too simple.

Feature Simple Analytics Umami
Price $19-199/month $0 (self-hosted)
Open source? No Yes
Custom events? Limited Full support
Real-time? Yes Yes
Self-hosted? No Yes

Verdict: Simple Analytics is for people who want “dead simple” analytics. Umami is for people who want “simple and customizable.”


Advanced: Custom Events (Because Page Views Aren’t Enough)

Umami supports custom events. This lets you track specific actions (button clicks, form submissions, purchases, etc.).

Tracking a Button Click

1
2
3
4
5
<button
onclick="umami('Button Click', { buttonName: 'signup' })"
>
Sign Up
</button>

In the Umami dashboard, you’ll see an “Events” section with:

  • Event name: Button Click
  • Event data: { buttonName: 'signup' }
  • Count: Number of times the event occurred

Tracking a Form Submission

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
document.getElementById('contact-form').addEventListener('submit', (e) => {
e.preventDefault();

// Track the form submission
umami('Form Submission', {
formName: 'contact',
formData: {
// Don't track personal data!
// Only track generic info
hasEmail: true,
hasPhone: false,
}
});

// Actually submit the form
e.target.submit();
});

Tracking a Purchase (E-commerce)

1
2
3
4
5
6
7
// After a successful purchase
umami('Purchase', {
orderId: '12345',
amount: 99.99,
currency: 'USD',
items: 3,
});

In the Umami dashboard, you can filter by event name (Purchase) and see:

  • Total purchases
  • Total revenue
  • Average order value
  • Revenue over time

Privacy note: Don’t track personal data (names, emails, addresses). Only track aggregate data.


Production Deployment (How I Deploy Umami)

Here’s the actual setup I use for production (don’t just copy-paste,

Architecture

1
2
3
4
5
6
7
[Internet]

[Nginx Reverse Proxy] (SSL termination)

[Umami Docker Container] (port 3000)

[PostgreSQL Docker Container] (port 5432)

Docker Compose (Production)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
version: '3.8'

services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: umami
restart: always
ports:
- "127.0.0.1:3000:3000" # Only bind to localhost (Nginx will proxy)
environment:
- DATABASE_URL=postgresql://umami:${POSTGRES_PASSWORD}@postgres:5432/umami
- HASH_SALT=${UMAMI_HASH_SALT} # Generate a random string!
- DATABASE_TYPE=postgresql
volumes:
- umami_data:/app/data
depends_on:
- postgres
networks:
- umami-network

postgres:
image: postgres:16-alpine
container_name: umami-postgres
restart: always
environment:
- POSTGRES_USER=umami
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=umami
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- umami-network

# Optional: Backup service
backup:
image: postgres:16-alpine
container_name: umami-backup
depends_on:
- postgres
volumes:
- ./backups:/backups
environment:
- PGPASSWORD=${POSTGRES_PASSWORD}
entrypoint: |
bash -c "
pg_dump -h postgres -U umami umami > /backups/umami-$$(date +%Y%m%d).sql
"
restart: "no"
networks:
- umami-network

volumes:
umami_data:
postgres_data:

networks:
umami-network:
driver: bridge

Nginx Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
listen 443 ssl http2;
server_name analytics.yourdomain.com;

# SSL (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

# Proxy to Umami
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

# Redirect HTTP to HTTPS
server {
listen 80;
server_name analytics.yourdomain.com;
return 301 https://$server_name$request_uri;
}

Environment Variables (.env File)

1
2
3
4
5
6
7
# Generate a random HASH_SALT:
# openssl rand -base64 32
UMAMI_HASH_SALT=your_random_salt_string_here

# Generate a strong PostgreSQL password:
# openssl rand -base64 24
POSTGRES_PASSWORD=your_strong_password_here

Backup Cron Job

1
2
# Add to crontab (runs daily at 2 AM)
0 2 * * * cd /path/to/umami && docker-compose up backup && docker-compose down backup

A Personal Anecdote (Because Why Not)

I remember the exact moment I decided to switch all my websites to Umami.

It was a Tuesday. I got an email from a German customer of my SaaS:

“Hallo. I notice your website uses Google Analytics. This is illegal in Germany under GDPR unless you have explicit consent. I am reporting you to the Datenschutzbehörde (data protection authority). Bitte entfernen Sie Google Analytics sofort.”

(My German is rusty,
I spent the next 3 days trying to make Google Analytics “GDPR compliant.” Spoiler: You can’t. Google Analytics transfers data to the US, which violates GDPR (Schrems II ruling). No amount of “we anonymize IPs” fixes that.

So I ripped out Google Analytics. Replaced it with Umami. Took me 2 hours (including Docker setup and Nginx config).

The German customer sent me a follow-up email:

“Danke! I can now visit your website without my data being sent to Google. Subscribed to your SaaS. :)”

That’s when I realized: Privacy isn’t just a legal requirement. It’s a competitive advantage.

Umami lets me say: “We respect your privacy. No cookies. No tracking. No selling your data.” That’s worth more than all of Google Analytics’ “advanced features” combined.


Getting Started Checklist

Ready to ditch Google Analytics? Here’s your roadmap:

  • Self-host Umami (Docker Compose is easiest)
  • Set up PostgreSQL (don’t use SQLite for production)
  • Configure Nginx (reverse proxy + SSL)
  • Add the tracking script to your website
  • Set up backups (PostgreSQL dumps)
  • Remove Google Analytics (delete the property,
  • Update your privacy policy (mention that you use Umami)
  • Celebrate (no more cookie banners!)

Resources


Final Thoughts

Umami represents something rare in today’s web: respect for your visitors.

Google Analytics treats your visitors like products to be tracked, analyzed, and sold to advertisers. Umami treats your visitors like humans who deserve privacy.

Is Umami perfect? No. The UI can be buggy. The self-hosted version requires maintenance (updates, backups, etc.). The cloud version is $9/month (which, okay, isn’t expensive,
But it’s yours. Your data stays on your server. Your visitors’ data isn’t sold to Google. And you don’t need a cookie banner that covers half the screen.

If you care about privacy (yours and your visitors’), switch to Umami. Your conscience will thank you. Your visitors will thank you. And if you’re in the EU, the Datenschutzbehörde won’t knock on your door.


P.S. If you’re from Google’s Analytics team and you’re reading this: Your product used to be amazing. Then you enshittified it. Fix it, or more of us will leave. (We’re already leaving.)

P.P.S. To the Umami team: Thank you for building an analytics tool that doesn’t make me feel like a creep. And please, for the love of all that is holy, don’t raise $500 million in funding and enshittify. We’ve seen how that movie ends. 🤐


Now go forth and analytics all the things. Privately. 📊