Skip to main content

How to Self-Host Umami: Simple Google Analytics Alternative 2026

·OSSAlt Team
umamianalyticsgoogle-analyticsself-hostingdockerprivacy2026

TL;DR

Umami is the simplest self-hosted analytics alternative to Google Analytics — no cookies, no personal data, GDPR compliant out of the box. MIT license, ~22K GitHub stars, built with Next.js and PostgreSQL. The tracking script is ~2KB. Deploy in 10 minutes with Docker Compose. If Plausible feels too complex (it needs ClickHouse), Umami is the right choice — just Postgres and a Node.js app.

Key Takeaways

  • Umami: MIT license, ~22K stars, Next.js + Postgres, no cookies needed
  • vs Plausible: Simpler to self-host (Postgres only, no ClickHouse), but fewer analytics features
  • Script size: ~2KB vs Google Analytics' 45KB
  • GDPR: No personal data collected, no cookie consent banner required
  • Setup time: 10 minutes with Docker Compose
  • Multi-site: One Umami instance tracks unlimited websites

Umami vs Plausible vs Google Analytics

FeatureUmamiPlausibleGoogle Analytics 4
LicenseMITAGPL 3.0Proprietary
DatabasePostgreSQLPG + ClickHouseCloud
Tracking script~2KB<1KB~45KB
CookiesNoneNoneYes (consent required)
GDPR✅ Compliant✅ CompliantRequires consent
Setup complexityLowMedium (ClickHouse)N/A
Funnels
Custom events
GitHub Stars~22K~21K
Monthly cost~$3–6 VPS~$5–10 VPSFree (data to Google)

Choose Umami when: You want the simplest possible privacy-respecting analytics with minimal ops overhead. Choose Plausible when: You need funnels, better retention charts, and a more polished dashboard and are okay managing ClickHouse.


Part 1: Docker Compose Setup

Umami requires only two services: the app and PostgreSQL.

# docker-compose.yml
version: '3.8'

services:
  umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    container_name: umami
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://umami:${POSTGRES_PASSWORD}@db:5432/umami
      DATABASE_TYPE: postgresql
      APP_SECRET: "${APP_SECRET}"    # Generate: openssl rand -hex 32
      DISABLE_TELEMETRY: 1
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
    volumes:
      - umami_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U umami"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  umami_db:
# .env
POSTGRES_PASSWORD=strong-database-password
APP_SECRET=$(openssl rand -hex 32)
docker compose up -d

Visit http://your-server:3000 → log in with default credentials:

  • Username: admin
  • Password: umami

Change the password immediately in Settings → Profile.


Part 2: HTTPS with Caddy

analytics.yourdomain.com {
    reverse_proxy localhost:3000
}

Part 3: Add Your First Website

  1. Settings → Websites → Add website
  2. Enter domain: yourdomain.com
  3. Name: My Website
  4. Copy the tracking snippet
<!-- Add to your website's <head>: -->
<script
  async
  defer
  data-website-id="your-website-id-here"
  src="https://analytics.yourdomain.com/script.js">
</script>

Since the script is served from your own domain (not umami.is), it bypasses ad blockers that target the umami.is CDN.

Next.js Integration

// app/layout.tsx
import Script from 'next/script';

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

WordPress Integration

Install the Umami Analytics plugin:

  1. Plugins → Add New → search "Umami Analytics"
  2. Settings → Umami Analytics → enter your Website ID and script URL

Part 4: Custom Events

Track button clicks, signups, and custom interactions:

<!-- HTML attribute-based tracking (no JS required): -->
<button
  data-umami-event="signup-click"
  data-umami-event-plan="pro">
  Sign Up for Pro
</button>

<!-- Or via JavaScript: -->
<script>
  document.getElementById('cta').addEventListener('click', function() {
    umami.track('cta-click', { location: 'hero', variant: 'blue' });
  });
</script>

Track Page Views in SPAs

Umami auto-tracks page views on navigation. For custom SPA route changes:

// Manually trigger page view:
umami.track('/dashboard');

// With custom data:
umami.track(props => ({
  ...props,
  url: '/checkout',
  title: 'Checkout Page',
}));

Part 5: Multiple Sites and Team Access

Add Multiple Websites

One Umami instance handles unlimited sites:

  • Settings → Websites → Add website for each domain
  • Each site gets a unique data-website-id
  • All visible from one dashboard

User Management

Create team members with specific access:

  • Settings → Users → Create user
  • Assign websites each user can view
  • Viewer role: read-only access to assigned sites
  • Admin: full access

Share Dashboard Publicly

Create a shareable link for clients or stakeholders:

  • Website → Edit → Enable share URL
  • Get a public read-only link: https://analytics.yourdomain.com/share/abc123
  • No login required for viewers

Part 6: Umami API

Umami exposes a stats API for embedding data in dashboards:

# Get API token:
curl -X POST https://analytics.yourdomain.com/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username": "admin", "password": "your-password"}'
# Returns: {"token": "your-jwt-token"}

# Get page views (last 24 hours):
curl "https://analytics.yourdomain.com/api/websites/{websiteId}/stats?startAt=0&endAt=86400000" \
  -H "Authorization: Bearer your-jwt-token"

# Get top pages:
curl "https://analytics.yourdomain.com/api/websites/{websiteId}/metrics?type=url&startAt=0&endAt=86400000" \
  -H "Authorization: Bearer your-jwt-token"

# Get active visitors (real-time):
curl "https://analytics.yourdomain.com/api/websites/{websiteId}/active" \
  -H "Authorization: Bearer your-jwt-token"

Part 7: Self-Hosted vs Umami Cloud

Self-HostedUmami Cloud
CostVPS cost (~$3–6/month)$9–$29/month
Setup time10 minutesInstant
Data ownership100% yoursUmami's servers
ScalingManualAutomatic
UpdatesManual docker pullAutomatic
WebsitesUnlimited3–20

For developers comfortable with Docker: self-host. For non-technical users or teams who don't want to maintain infrastructure: Umami Cloud at $9/month is reasonable.


Maintenance

# Update Umami:
docker compose pull
docker compose up -d

# Backup:
docker exec umami-db pg_dump -U umami umami | \
  gzip > umami-backup-$(date +%Y%m%d).sql.gz

# Restore:
gunzip -c umami-backup-YYYYMMDD.sql.gz | \
  docker exec -i umami-db psql -U umami -d umami

See all open source Google Analytics alternatives at OSSAlt.com/alternatives/google-analytics.

Comments