Umami: Simple, Private Web Analytics You Can Self-Host
Google Analytics is everywhere. It's on over half of all websites, and for good reason — it's powerful, it's free, and it's what everyone knows. But that ubiquity comes at a cost that's increasingly hard to ignore.
Your visitors' data flows to Google's servers, where it feeds ad targeting across the web. Your site needs a cookie consent banner in the EU (and increasingly elsewhere). GA4's interface requires a certification course to navigate. And if you're running a small or medium website, 95% of Google Analytics' features are noise you'll never touch.
Umami is a different kind of analytics tool. It's open source, self-hosted, privacy-focused, and radically simple. No cookies, no consent banners, no personal data collection — just clean metrics about how people use your site, presented on a single dashboard you can understand at a glance.
What Is Umami?
Umami is a self-hosted web analytics application built with Next.js and Prisma. It was created by Mike Cao in 2020 as a response to the growing complexity of Google Analytics and increasing privacy regulations.
The core philosophy is minimalism: track what matters, respect visitor privacy, and keep the interface clean enough that you actually use it.
Here's what makes Umami different:
- No cookies — Umami doesn't use cookies or any form of persistent browser storage. There's nothing to consent to.
- No personal data — Visitors are identified by a hash of the IP address + User-Agent, rotated daily. No profiles, no cross-site tracking.
- Tiny script — The tracking script is under 2 KB (gzipped), compared to Google Analytics' 45+ KB.
- GDPR-compliant by default — Because no personal data is collected or stored, you don't need a cookie banner or privacy policy addendum for analytics.
- Open source — MIT licensed. The code is on GitHub, and you can audit every line.
- Multiple sites — Track as many websites as you want from a single installation.
Umami also offers a hosted cloud version if you don't want to manage your own server, but self-hosting is where the tool really shines — zero cost for unlimited sites and unlimited traffic.
Umami vs Plausible vs Google Analytics
If you're exploring privacy-friendly analytics, you've probably also come across Plausible. Both are excellent tools, but they make different trade-offs.
| Feature | Google Analytics | Plausible | Umami |
|---|---|---|---|
| Script size | ~45 KB | < 1 KB | < 2 KB |
| Cookie usage | Yes | No | No |
| GDPR compliance | Requires consent | By default | By default |
| Data ownership | Google's servers | Your server (or theirs) | Your server (or theirs) |
| Custom events | Yes (complex) | Yes | Yes |
| Funnels | Yes | Yes (paid/self-hosted) | No |
| Revenue tracking | Yes | Yes | No |
| API access | Yes | Yes | Yes |
| Dashboard | Complex, multi-page | Single page | Single page |
| Database | N/A | ClickHouse + PostgreSQL | PostgreSQL or MySQL |
| Self-hosting complexity | N/A | Moderate (3 containers) | Simple (2 containers) |
| Self-hosting RAM | N/A | 2-4 GB minimum | 512 MB - 1 GB minimum |
| Cloud pricing | Free | From $9/month | From $9/month |
| License | Proprietary | AGPL | MIT |
The key differences between Umami and Plausible:
Umami is lighter. It uses PostgreSQL or MySQL as its only data store — no ClickHouse needed. This means you can run it on a 1 GB VPS, which is significant for budget self-hosters. Plausible's ClickHouse dependency means it needs at least 2 GB RAM to be stable.
Plausible has more features. Plausible offers funnels, revenue tracking, and some reporting features that Umami doesn't. If you need those, Plausible is the better fit.
Umami has a more permissive license. Umami uses MIT. Plausible uses AGPL, which has implications if you want to modify and distribute the code.
Both are excellent choices. If you already run PostgreSQL and want the simplest possible setup, Umami is hard to beat. If you need more advanced analytics features and don't mind the extra resource overhead, Plausible is a great option too.
Installation with Docker Compose
Umami's Docker setup is straightforward. You need two containers: the Umami application and a PostgreSQL database.
Prerequisites
- A Linux server with Docker and Docker Compose installed
- A domain name pointed at your server (e.g.,
analytics.yourdomain.com) - Roughly 1 GB of RAM available (Umami itself uses very little)
Docker Compose file
Create a directory for your Umami installation and add this docker-compose.yml:
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://umami:your-secure-password@db:5432/umami
DATABASE_TYPE: postgresql
APP_SECRET: your-random-secret-string
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: your-secure-password
volumes:
- umami-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
umami-db:
Generate a proper secret for APP_SECRET:
openssl rand -base64 32
Replace your-secure-password with a strong database password and your-random-secret-string with the generated secret.
Start the services
docker compose up -d
That's it — two containers. Umami is now running on port 3000.
MySQL alternative
If you prefer MySQL, swap the image tags and database configuration:
services:
umami:
image: ghcr.io/umami-software/umami:mysql-latest
environment:
DATABASE_URL: mysql://umami:your-secure-password@db:3306/umami
DATABASE_TYPE: mysql
APP_SECRET: your-random-secret-string
# ... rest stays the same
db:
image: mysql:8
environment:
MYSQL_DATABASE: umami
MYSQL_USER: umami
MYSQL_PASSWORD: your-secure-password
MYSQL_ROOT_PASSWORD: your-root-password
volumes:
- umami-db:/var/lib/mysql
PostgreSQL is the recommended choice. It's what the Umami team primarily tests against, and it tends to perform better for analytics workloads.
Initial Setup
Creating your account
Visit http://your-server:3000 (or your domain, once you've set up a reverse proxy). You'll see the login page.
The default credentials are:
- Username:
admin - Password:
umami
Log in and immediately change the password under Settings > Profile.
Adding your first website
- Go to Settings > Websites
- Click Add website
- Enter a name (for your reference) and the domain
- Click Save
Umami generates a unique tracking code for each site. Click the site you just added to see your tracking script.
Adding the Tracking Script
Basic HTML
Add the tracking script to the <head> of your website. Umami gives you the exact snippet:
<script defer src="https://analytics.yourdomain.com/script.js"
data-website-id="your-website-id"></script>
The data-website-id is a UUID that Umami generates for each site. It's safe to expose in your HTML — it only identifies which site the data belongs to.
Next.js
In Next.js with the App Router, add it to your root layout:
// app/layout.tsx
import Script from "next/script";
export default function RootLayout({ children }) {
return (
<html>
<head>
<Script
defer
src="https://analytics.yourdomain.com/script.js"
data-website-id="your-website-id"
strategy="afterInteractive"
/>
</head>
<body>{children}</body>
</html>
);
}
Astro
In Astro, add it to your base layout component:
---
// src/layouts/BaseLayout.astro
---
<html>
<head>
<script defer
src="https://analytics.yourdomain.com/script.js"
data-website-id="your-website-id"></script>
</head>
<body>
<slot />
</body>
</html>
Hugo
In Hugo, add the script to your layouts/partials/head.html:
<script defer src="https://analytics.yourdomain.com/script.js"
data-website-id="your-website-id"></script>
WordPress
You can use a plugin like "Insert Headers and Footers" or add it directly to your theme's header.php. Alternatively, the community maintains a dedicated Umami WordPress plugin.
Dashboard Walkthrough
Once your tracking script is collecting data, your Umami dashboard shows:
Core metrics (at a glance)
- Visitors — Unique visitors (based on the daily-rotating hash)
- Page views — Total page loads
- Bounce rate — Percentage of single-page sessions
- Average visit time — How long people stay
Detailed breakdowns
- Pages — Which pages get the most traffic, with view counts
- Referrers — Where your visitors come from (direct, search engines, social, links)
- Browsers — Chrome, Firefox, Safari, etc.
- Operating systems — Windows, macOS, Linux, Android, iOS
- Devices — Desktop, mobile, tablet
- Countries — Geographic distribution (based on IP geolocation, no precision beyond country)
- Languages — Browser language settings
- Events — Custom events you've set up (more on this below)
Realtime view
Umami shows a live count of current visitors and a realtime event stream. It's not as elaborate as Google Analytics' Realtime report, but it's useful for seeing immediate traffic after sharing a link or publishing content.
Time ranges
You can view data for today, yesterday, the last 7 days, last 30 days, last 90 days, this month, last month, or any custom date range. Umami stores data indefinitely by default, so your historical data grows over time.
Custom Events
Page views are tracked automatically, but you often want to know about specific interactions — button clicks, form submissions, file downloads, feature usage.
Tracking events in JavaScript
Umami provides a global umami object for custom event tracking:
// Track a simple event
umami.track("signup-button-click");
// Track an event with custom properties
umami.track("purchase", {
product: "Pro Plan",
price: 29.99,
currency: "USD"
});
// Track a form submission
document.querySelector("#contact-form").addEventListener("submit", () => {
umami.track("contact-form-submit");
});
Using data attributes
For simpler cases, you can track events declaratively with HTML attributes — no JavaScript required:
<button data-umami-event="signup-click">Sign Up</button>
<a href="/pricing" data-umami-event="pricing-link"
data-umami-event-source="homepage">View Pricing</a>
<button data-umami-event="download"
data-umami-event-file="whitepaper.pdf">Download PDF</button>
Any element with data-umami-event automatically sends an event when clicked. Additional data-umami-event-* attributes are sent as custom properties.
Viewing event data
Events appear in the Events section of your dashboard. You can see event counts over time and filter by event name. Custom properties are visible when you click into an individual event type.
Teams and Sharing
Multiple users
Umami supports multiple user accounts with different permission levels:
- Admin — Full access to all sites and settings
- User — Access only to assigned sites
Create additional users under Settings > Users. This is useful if you manage analytics for multiple clients or have a team that needs dashboard access.
Team workspaces
Umami supports teams — groups of users that share access to a set of websites. Each team has its own set of websites and members, making it easy to organize access by project, client, or department.
Public dashboards
You can share any website's dashboard publicly by enabling the share URL in the site settings. This generates a unique link anyone can access without logging in — useful for transparently sharing your site's traffic numbers.
Some open source projects and indie makers use public Umami dashboards to share their traffic stats as a signal of credibility.
Performance and Resource Usage
This is where Umami really differentiates itself. It's remarkably lightweight.
Server resources
In practice, a typical Umami installation tracking a few sites with moderate traffic (under 100K pageviews/month) uses:
| Resource | Usage |
|---|---|
| RAM (Umami app) | 100-200 MB |
| RAM (PostgreSQL) | 100-300 MB |
| CPU | Negligible (< 5% on a single core) |
| Disk (database) | ~1 GB per million pageviews |
| Network | Minimal |
Compare this to Plausible, which needs ClickHouse and recommends 2-4 GB of RAM minimum. Umami can comfortably run on a $4-6/month VPS alongside other services.
Client-side impact
The tracking script is under 2 KB gzipped. It loads asynchronously with the defer attribute, so it never blocks page rendering. There's no visual impact on your site — no cookie banner, no loading indicator, nothing.
For comparison:
| Tool | Script size (gzipped) |
|---|---|
| Google Analytics (GA4) | ~45 KB |
| Matomo | ~22 KB |
| Plausible | < 1 KB |
| Umami | < 2 KB |
| Fathom | ~6 KB |
Plausible edges out Umami on raw script size, but both are so small that the difference is imperceptible. Either is a massive improvement over GA4.
Comparison: Umami vs Plausible vs Matomo vs Fathom vs GoAccess
If you're evaluating self-hosted analytics, here's how the main options stack up:
| Feature | Umami | Plausible | Matomo | Fathom | GoAccess |
|---|---|---|---|---|---|
| Type | Web app | Web app | Web app | Web app | Log analyzer |
| License | MIT | AGPL | GPL | Proprietary | MIT |
| Cookies | No | No | Optional | No | N/A (server-side) |
| GDPR by default | Yes | Yes | Configurable | Yes | Yes |
| Script size | < 2 KB | < 1 KB | ~22 KB | ~6 KB | None (server logs) |
| Custom events | Yes | Yes | Yes | Yes | No |
| Funnels | No | Yes | Yes | No | No |
| Heatmaps | No | No | Yes (plugin) | No | No |
| Session recording | No | No | Yes (plugin) | No | No |
| E-commerce tracking | No | Revenue goals | Yes | No | No |
| Self-host complexity | Low | Moderate | High | N/A (SaaS only) | Very low |
| Min RAM (self-hosted) | 512 MB | 2 GB | 2 GB | N/A | 64 MB |
| Database | PostgreSQL/MySQL | PostgreSQL + ClickHouse | MySQL/MariaDB | N/A | None (flat files) |
| API | Yes | Yes | Yes | Yes | No |
| Cloud option | Yes ($9+/mo) | Yes ($9+/mo) | Yes ($19+/mo) | Yes ($14+/mo) | No |
| Multi-site | Yes | Yes | Yes | Yes (paid) | Manual |
| Realtime | Yes | Yes | Yes | Yes | Yes |
| Best for | Simplicity seekers | Feature + privacy balance | GA replacement | Set-and-forget | Terminal lovers |
Quick takes
Matomo is the closest to a full Google Analytics replacement. It can do heatmaps, session recordings, funnels, A/B testing, and e-commerce tracking. But it's also the most complex to run, and its PHP + MySQL stack feels dated compared to the newer options. If you need GA-level features with self-hosting, Matomo is your answer. If you don't need all that, it's overkill.
Fathom is privacy-focused and polished, but it's SaaS-only now — the self-hosted open source version is no longer maintained. If you want self-hosting, Fathom is off the table.
GoAccess is a completely different animal. It analyzes your web server's access logs directly — no tracking script, no JavaScript, no database. It runs in the terminal or generates a static HTML report. It's brilliant for quick server-level analytics and has essentially zero overhead, but it can't track events, doesn't understand SPAs, and the UX is spartan.
Umami and Plausible are the two strongest options for privacy-focused, self-hosted web analytics. Choose Umami for lower resource usage and simplicity. Choose Plausible for more advanced features like funnels and revenue tracking.
Tips for Production Use
Use a custom domain for the tracking script
Ad blockers often target known analytics domains. If you run Umami on analytics.yourdomain.com, some visitors will block requests to it. Using a subdomain of your own site (like stats.yourdomain.com) or proxying the script through your main domain reduces this.
Reverse proxy with Caddy
Put Umami behind a reverse proxy with automatic HTTPS. With Caddy:
analytics.yourdomain.com {
reverse_proxy localhost:3000
}
Caddy handles Let's Encrypt certificates automatically.
Reverse proxy with Nginx
server {
listen 443 ssl http2;
server_name analytics.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Proxy the script through your own domain
To further reduce ad blocker interference, you can proxy the Umami script through your own site. In Nginx:
# Add to your main site's Nginx config
location /stats/script.js {
proxy_pass https://analytics.yourdomain.com/script.js;
proxy_set_header Host analytics.yourdomain.com;
}
Then reference the proxied script in your HTML:
<script defer src="/stats/script.js"
data-website-id="your-website-id"></script>
This makes the analytics script appear to come from your own domain, which most ad blockers will allow.
Using the Umami API
Umami has a REST API for extracting data programmatically. Authenticate with a token, then query any metric:
# Get an authentication token
TOKEN=$(curl -s -X POST https://analytics.yourdomain.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"your-password"}' | jq -r '.token')
# Get pageviews for the last 7 days
curl -s "https://analytics.yourdomain.com/api/websites/{website-id}/pageviews?startAt=$(date -d '7 days ago' +%s)000&endAt=$(date +%s)000&unit=day" \
-H "Authorization: Bearer $TOKEN"
The API is useful for building custom dashboards, generating reports, or integrating analytics data into other tools.
Database backups
Back up your PostgreSQL database regularly. A simple cron job works:
# Add to crontab: daily backup at 3 AM
0 3 * * * docker exec umami-db-1 pg_dump -U umami umami | gzip > /backups/umami-$(date +\%Y\%m\%d).sql.gz
Data retention
Umami stores data indefinitely by default. If you want to limit storage growth, you can periodically clean old data through direct SQL queries on the database. There's no built-in retention policy in the UI, but the database is straightforward — events are stored in a website_event table with timestamps.
The Honest Take
What you give up compared to Google Analytics
Let's be direct about the gaps:
- No funnel analysis — You can't define multi-step conversion funnels. Plausible and Matomo can.
- No cohort analysis — No way to track how groups of users behave over time.
- No e-commerce integration — No revenue tracking, no product performance reports.
- No ad platform integration — If you run Google Ads, Facebook Ads, or similar, GA4's attribution modeling is something Umami can't replace.
- No A/B testing — No built-in experiment framework.
- No heatmaps or session recordings — You won't see where people click or how they scroll. Matomo offers this; Umami doesn't.
- Less granular user data — Since there are no cookies or persistent identifiers, returning visitor tracking is approximate. GA can follow a user across sessions; Umami cannot.
- Smaller ecosystem — Fewer integrations, fewer plugins, less community content compared to GA's massive ecosystem.
When Umami is the right choice
Despite those gaps, Umami is the right tool for a large number of use cases:
- Content sites, blogs, documentation — You want to know which pages are popular and where traffic comes from. Umami gives you exactly this.
- Side projects and indie products — You don't need enterprise analytics. You need a lightweight dashboard that tells you if anyone is using your thing.
- Privacy-conscious sites — You've decided (or your users have decided) that sending data to Google isn't acceptable. Umami gives you analytics without the ethical compromise.
- Multi-site operators — Running 10, 20, 50 sites? Umami handles them all from a single installation without per-site fees.
- Low-resource environments — Running on a budget VPS that also hosts your app? Umami's ~300 MB footprint won't crowd out your other services.
- Developers who want an API — Umami's API is clean and well-documented. If you want to build custom analytics integrations, it's a good foundation.
The bottom line
Umami won't replace Google Analytics for a marketing team that lives in attribution reports and conversion funnels. It's not trying to. What it does is provide the analytics that 90% of website owners actually look at — page views, traffic sources, popular pages, device breakdown — in a package that's private, fast, and simple enough that you'll actually check it.
If your current analytics workflow is "open Google Analytics, feel overwhelmed, close the tab," Umami might be exactly what you need. And since it runs on a $5 VPS with a five-minute Docker setup, trying it costs almost nothing.
Resources
- Umami documentation
- Umami GitHub repository
- Umami API reference
- Umami cloud — hosted option if you don't want to self-host