Back to Blog
AnalyticsPrivacyGDPRSelf-HostingNuxt

From Google Analytics to Umami: Cookie-Free Analytics, Self-Hosted

6 min readDipl. Ing. Borislav Ćulum

My website ran on Google Analytics for a long time — consent banner and cookie management included. After switching to Umami Analytics, all of that is gone. No banner, no cookies, no data in the US. Here's why I switched and what the migration actually looked like.

The Problem with Google Analytics

Google Analytics is powerful. But for a straightforward business website, it comes with a lot of overhead:

  • ~50 KB tracking script that affects load times
  • Cookie banner required under GDPR — every visitor must actively consent
  • Data transfer to the US — legally problematic, especially for Austrian and German businesses
  • Consent rate below 50% — half of all visitors are invisible in GA because they declined

That last point bothered me most. I could only see a fraction of my actual visitors because the other half dismissed the banner.

Why Umami

Umami is an open-source web analytics alternative with a clear focus:

  • Cookie-free — no cookies, no consent banner required
  • ~2 KB script — 25× smaller than GA4
  • Privacy-compliant by default — no personal data, no plain-text IP addresses stored
  • Self-hosted — full control, data stays on my servers
  • MIT license — free, no hidden costs
  • Multi-site — one instance for multiple projects

The key point: Umami can be operated without consent under GDPR and Austrian TKG 2021 because no personal data is processed. No banner, no consent management, no added complexity.

My Architecture

I run Umami on the same Hetzner VPS as my other tools:

analytics.example.com
    ↓ nginx (reverse proxy, Let's Encrypt SSL)
Docker Container (Umami Node.js app)
    ↓ SSL connection
Hetzner Managed PostgreSQL
    └── Schema: umami (isolated from everything else)

Deliberate decisions:

  • No dedicated Postgres — I reuse the existing Hetzner Managed DB with a dedicated umami schema, fully isolated from the rest of the database

The Migration in Detail

1. Prepare Infrastructure

Create the schema in the existing Postgres database:

CREATE SCHEMA IF NOT EXISTS umami;
GRANT USAGE, CREATE ON SCHEMA umami TO youruser;

Docker Compose for Umami (/opt/umami/docker-compose.yml):

services:
  umami:
    image: docker.umami.is/umami-software/umami:postgresql-latest
    container_name: umami
    restart: unless-stopped
    ports:
      - '127.0.0.1:3002:3000'
    environment:
      DATABASE_URL: ${DATABASE_URL}
      DATABASE_TYPE: postgresql
      APP_SECRET: ${APP_SECRET}
      DISABLE_TELEMETRY: '1'
      NODE_EXTRA_CA_CERTS: /etc/ssl/certs/hetzner-ca.crt
    volumes:
      - /opt/umami/ca.crt:/etc/ssl/certs/hetzner-ca.crt:ro

I extracted the Hetzner Managed DB server certificate directly from the TLS handshake and trusted it via NODE_EXTRA_CA_CERTS:

openssl s_client -connect db-host:5432 -starttls postgres 2>/dev/null </dev/null \
  | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
  | tee /opt/umami/ca.crt

2. nginx vhost + SSL

server {
    listen 443 ssl http2;
    server_name analytics.example.com;
    location / {
        proxy_pass http://127.0.0.1:3002;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

SSL via Certbot, as usual.

3. Nuxt Integration

In nuxt.config.ts — the script is only injected when the env var is set:

script: process.env.NUXT_PUBLIC_UMAMI_ID ? [
  {
    src: 'https://analytics.example.com/script.js',
    'data-website-id': process.env.NUXT_PUBLIC_UMAMI_ID,
    defer: true,
  },
] : [],

Plus a small composable for custom events:

// composables/useUmami.ts
export function trackEvent(
  event: string,
  data?: Record<string, unknown>,
): void {
  if (import.meta.client && window.umami) {
    window.umami.track(event, data)
  }
}

Since Umami sets no cookies, the banner is simply no longer needed. Two lines removed from the layouts — done.

What I Gained

Google AnalyticsUmami
Script size~50 KB~2 KB
CookiesYes (consent required)No
Consent bannerYesNo
Data locationGoogle (US)Own server (DE)
Visible visitors~50%100%
CostFree (data = price)Self-hosted
Setup effortLowMedium (one-time)

The biggest practical win: complete visitor visibility. No visitors disappearing through the consent banner.

When Umami Is Not Enough

Umami is not a GA replacement for every use case. If you need any of the following, stick with GA or choose a different solution:

  • Deep conversion funnels with attribution (e.g. Google Ads integration)
  • Remarketing audiences for Google/Meta Ads
  • E-commerce tracking with revenue data

For a business website, a blog, or a SaaS dashboard, Umami is more than sufficient.

Conclusion

The migration took a few hours and was worth it. I now see more data than before — no banner, no cookies, no data at Google. For projects that take GDPR compliance seriously, Umami is a clear recommendation.

BC

Dipl. Ing. Borislav Ćulum

Managing Director & Senior Software Developer

With over 13 years of experience in enterprise software development, I have supported numerous companies like SPAR, Robert Bosch, and Sony DADC in modernizing their IT systems. My expertise includes Java/Spring Boot, microservices architectures, SAP integration, and e-invoice solutions.

13+ Years ExperienceJava & Spring BootEnterprise Development

Need privacy-compliant web development?

I build and operate web applications that are GDPR-compliant and performant — from architecture to deployment.

Contact