Google Analytics is powerful, but it comes with a cost: your visitors’ privacy. Umami is a self-hosted analytics platform that gives you the insights you need without cookies, fingerprinting, or sending data to third parties. It’s lightweight, GDPR-compliant out of the box, and looks great doing it.
What is Umami?
Umami is an open-source, privacy-focused web analytics tool built with Next.js and PostgreSQL (or MySQL). It tracks page views, referrers, devices, and more — all without cookies or personal data collection. The dashboard is clean, fast, and does exactly what you need without the bloat of Google Analytics.
Why Replace Google Analytics?
- No cookies: Umami doesn’t use cookies, so no annoying consent banners
- GDPR/CCPA compliant: No personal data collection by default
- Lightweight script: The tracking script is under 2KB — won’t slow down your site
- Own your data: Everything stays on your server
- No data sampling: Google Analytics free tier samples heavy-traffic sites; Umami doesn’t
- Simple UI: See what matters without drowning in 200 reports you’ll never read
- Unlimited websites: Track as many sites as you want, no per-site pricing
Prerequisites
Before starting, you’ll need:
- A Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
- Docker and Docker Compose installed
- A domain name (optional but recommended)
- A reverse proxy like Nginx Proxy Manager or Traefik (for HTTPS)
- Basic terminal knowledge
Step 1: Create the Project Directory
mkdir -p ~/umami && cd ~/umami
Step 2: Create the Docker Compose File
Create docker-compose.yml:
version: '3'
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: replace-me-with-a-random-string
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: your-secure-password
volumes:
- umami-db:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami"]
interval: 5s
timeout: 5s
retries: 5
volumes:
umami-db:
Important: Replace your-secure-password with a strong password (both places) and set APP_SECRET to a random string. Generate one with:
openssl rand -base64 32
Step 3: Start Umami
docker compose up -d
Umami takes about 30 seconds on first start to initialize the database. Check progress with:
docker compose logs -f umami
Look for Listening on http://0.0.0.0:3000 to confirm it’s ready.
Step 4: Log In and Change the Default Password
Open http://your-server-ip:3000 in your browser.
Default credentials:
- Username:
admin - Password:
umami
Change this immediately. Go to Settings → Profile and update your password.
Step 5: Add Your First Website
- Go to Settings → Websites
- Click Add website
- Enter your site name and domain (e.g.,
selfhostsetup.com) - Click Save
- Click the Edit icon next to your new site
- Copy the Tracking code — it looks like this:
<script async src="https://your-umami-domain.com/script.js" data-website-id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"></script>
Step 6: Add the Tracking Script to Your Site
Paste the tracking code into the <head> section of your website. Where exactly depends on your setup:
Hugo (PaperMod or similar):
Add to layouts/partials/extend_head.html:
<script async src="https://analytics.yourdomain.com/script.js" data-website-id="your-website-id"></script>
WordPress:
Use a plugin like “Insert Headers and Footers” or add it to your theme’s header.php.
Plain HTML:
Just paste it before </head> in every page.
Step 7: Set Up a Reverse Proxy (Recommended)
You’ll want HTTPS for the tracking script to work on HTTPS sites. If you’re using Nginx Proxy Manager:
- Add a new proxy host
- Set the domain (e.g.,
analytics.yourdomain.com) - Forward to
your-server-ip:3000 - Enable SSL with Let’s Encrypt
- Turn on Websockets Support
If you’re using Traefik or raw Nginx, point your subdomain to port 3000 with SSL termination.
Step 8: Verify It’s Working
Visit your website in a browser, then check the Umami dashboard. You should see your visit appear in real-time within a few seconds. If not:
- Check browser console for script loading errors
- Verify the domain in Umami settings matches your actual domain
- Make sure your ad blocker isn’t blocking the script (see tips below)
Avoiding Ad Blockers
Some ad blockers flag script.js from analytics subdomains. Umami has a built-in solution:
Rename the tracking script by setting the TRACKER_SCRIPT_NAME environment variable:
environment:
TRACKER_SCRIPT_NAME: custom-name
Then update your tracking code to reference /custom-name.js instead of /script.js.
Proxy through your own domain by adding a rewrite rule in your web server that forwards /stats.js to your Umami instance. This makes the script appear to come from your own domain.
Understanding the Dashboard
Umami’s dashboard gives you:
- Page views and visitors: Real-time and historical
- Referrers: Where your traffic comes from
- Browsers and OS: What your visitors use
- Devices: Desktop, mobile, tablet breakdown
- Countries: Geographic distribution
- Events: Custom event tracking (button clicks, form submissions)
- UTM parameters: Campaign tracking support
All of this without a single cookie.
Custom Event Tracking
Track button clicks, form submissions, or any interaction:
// Track a button click
document.getElementById('signup-btn').addEventListener('click', () => {
umami.track('signup-click');
});
// Track with custom data
umami.track('purchase', { product: 'pro-plan', price: 29 });
Events show up in the dashboard under the Events tab for each website.
Multiple Users and Teams
Umami supports multiple user accounts with different permission levels:
- Go to Settings → Users
- Click Add user
- Set their role (Admin or Viewer)
Viewers can see dashboards but can’t modify settings. Useful for sharing analytics with clients or team members without giving them admin access.
Backup Strategy
Your analytics data lives in the PostgreSQL volume. Back it up regularly:
# Dump the database
docker exec umami-db-1 pg_dump -U umami umami > umami-backup-$(date +%Y%m%d).sql
# Restore if needed
cat umami-backup.sql | docker exec -i umami-db-1 psql -U umami umami
Add this to a cron job for automated backups:
0 3 * * * docker exec umami-db-1 pg_dump -U umami umami > /backups/umami-$(date +\%Y\%m\%d).sql
Updating Umami
cd ~/umami
docker compose pull
docker compose up -d
Umami handles database migrations automatically on startup.
Troubleshooting
Dashboard shows no data:
- Verify the tracking script is loading (check browser Network tab)
- Confirm the
data-website-idmatches what’s in Umami settings - Check that the domain in Umami matches your site’s actual domain
Script blocked by ad blocker:
- Rename the tracker script via environment variable
- Proxy the script through your own domain
High memory usage:
- PostgreSQL defaults can be tuned for smaller servers
- Add
shm_size: 256mbto the db service if you see shared memory errors
Container won’t start:
- Check logs:
docker compose logs umami - Most common issue: database password mismatch between services
Umami vs Alternatives
| Feature | Umami | Plausible | Matomo |
|---|---|---|---|
| Cookie-free | ✅ | ✅ | Optional |
| Script size | ~2KB | ~1KB | ~22KB |
| Self-host difficulty | Easy | Easy | Moderate |
| Resource usage | Low | Low | High |
| Custom events | ✅ | ✅ | ✅ |
| Ecommerce tracking | Basic | Basic | Advanced |
| UI complexity | Simple | Simple | Complex |
Umami hits the sweet spot for most self-hosters: simple enough to set up in 10 minutes, powerful enough for real analytics, and light enough to run on a Raspberry Pi.
Conclusion
Umami gives you everything most websites actually need from analytics — page views, referrers, device stats, and custom events — without the privacy baggage of Google Analytics. It’s a 10-minute Docker deploy that respects your visitors and keeps your data where it belongs: on your server.
If you’re running a blog, SaaS landing page, or documentation site, Umami is probably all the analytics you need. And if you ever outgrow it, your data is in PostgreSQL — easy to export, easy to migrate.