How to Install Umami (Privacy-Friendly Website Analytics Without Google)

Umami: analytics without Google — Who reads what, no cookies, no banner.

You want to know whether anyone reads the blog. You don't want to hand every visitor to Google, add a cookie banner, or wade through a 40-tab analytics console. Umami is a self-hosted analytics server that counts page views, referrers, countries, devices and custom events with no cookies and no personal data, on one clean page. One container, one script tag, and GDPR stops being your problem.

Quick reference

TaskWhat
Folder~/docker/umami/compose.yaml
Webhttp://server-ip:3010
Default loginadmin / umami — change it
Add a siteSettings → Websites → Add → copy the tracking script
Eventsdata-umami-event="signup" on a button, or umami.track()
Public stats pagewebsite → Edit → Enable share URL

1. The compose file

mkdir -p ~/docker/umami && cd ~/docker/umami
nano compose.yaml

services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: umami
ports:
- "3010:3000"
environment:
DATABASE_URL: postgresql://umami:${DB_PASSWORD}@db:5432/umami
DATABASE_TYPE: postgresql
APP_SECRET: ${APP_SECRET}
depends_on:
- db
restart: unless-stopped

db:
image: postgres:16-alpine
container_name: umami_db
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ./db:/var/lib/postgresql/data
restart: unless-stopped

.env with DB_PASSWORD and APP_SECRET (openssl rand -hex 32).

docker compose up -d

2. Reverse proxy and HTTPS

Analytics must be reachable from the public site's visitors, so put it behind the proxy as stats.example.com (tutorial 28 or 57), or a Cloudflare Tunnel (109). Log in at https://stats.example.com as admin / umami and change the password under Profile immediately.

3. Add your site

Settings → Websites → Add website: name and domain. Then Edit → Tracking code gives you one line:

<script defer src="https://stats.example.com/script.js" data-website-id="a1b2c3..."></script>

Put it in the site's : in Hugo, a layouts/partials/extend_head.html (PaperMod) with that line; in WordPress, Appearance → Theme file editor → header.php or an "insert headers" plugin. Visit the site; the dashboard shows the hit within seconds.

4. Read the dashboard

Views, visitors, bounce rate and average time at the top; pages, referrers, browsers, OS, devices and countries below; a realtime view for when you've just posted something. Filter by any of them (click a referrer to see what those visitors read). Compare date ranges. That's genuinely all most sites need.

5. Events

Count clicks on a specific thing:

<a href="/download" data-umami-event="download-click" data-umami-event-file="guide.pdf">Download</a>

Or from JavaScript: umami.track('signup', { plan: 'free' }). Events appear under their own tab with the properties.

6. Share stats publicly

Website → Edit → Enable share URL. Anyone with the link sees a read-only dashboard — handy for a project site or for showing a client.

Why no banner?

Umami stores no cookies and no IP addresses (it hashes them with a daily-rotating salt to count unique visitors, then forgets them). Under GDPR/PECR that's generally considered not to need consent, which is why the dashboard is allowed to be this simple. Still mention it in your privacy page.

Ad blockers

Some block script.js by name. Serve it under another name: in the proxy, map /stats.js → /script.js, or set TRACKER_SCRIPT_NAME: visits.js in the environment and use that in the tag.

Troubleshooting

No hits appear. Script URL wrong or blocked; open the browser's network tab on your site and look for the request. Mixed content if the site is https and Umami isn't.

"Invalid website ID". You edited the tag after creating the site; copy it again.

Admin password forgotten. docker compose exec db psql -U umami -c "UPDATE \"user\" SET password='$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa' WHERE username='admin';" resets it to umami.

Database errors on first start. The db wasn't ready; docker compose restart umami.

Wrapping up

Two containers behind the proxy, change the admin password, one script tag in the site's head. Honest numbers about who reads what and where they came from, no cookies, no Google, and a dashboard you'll actually look at.

Call us