Devince
DevOps,  Privacy,  Self-hosting

Self-hosted Umami: analytics bez cookies, bez bannera, bez Google'a

Date Published

Mały, samodzielny serwer świecący ciepłym światłem na drewnianym warsztacie w cichym domowym workshopie o zmierzchu — wizualna metafora self-hosted analytics

Google Analytics na małej stronie wymaga cookie bannera, third-party skryptu z googletagmanager.com i wysyłki danych na serwery Google'a w USA. Po co ten cyrk dla 200 wizyt dziennie? No właśnie. Self-hosted Umami załatwia to samo na własnym VPS-ie: jeden mały kontener plus Postgres, zero cookies, dane na Twoim serwerze. Setup zajmuje 5 minut. Jeśli wiesz gdzie są pułapki.

Co potrzebujesz

  • VPS z Dockerem (~100 MB RAM na Umami + ~50 MB na Postgres)
  • Subdomena typu stats.example.com z A-recordem na IP VPS-a
  • Reverse proxy z auto-certem Let's Encrypt (Traefik, Caddy, NPM, cokolwiek już masz)

Ja postawiłem to przez Coolify (one-click w sekcji Services → Umami), ale to nie jest wymóg. Umami to publiczny obraz ghcr.io/umami-software/umami i działa na każdej maszynie z Dockerem.

4 kroki setupu

1. DNS. A-record stats.example.com → IP serwera. Sprawdź dig +short stats.example.com zanim ruszysz dalej — bez tego Let's Encrypt nie wystawi certa.

2. Umami + Postgres. Minimalny compose.yaml:

1services:
2 umami:
3 image: ghcr.io/umami-software/umami:3.0.3
4 environment:
5 DATABASE_URL: postgres://umami:strong-pass@db:5432/umami
6 APP_SECRET: ${APP_SECRET} # openssl rand -base64 32
7 TRACKER_SCRIPT_NAME: ascii.js # bypass adblock list
8 DISABLE_TELEMETRY: 1
9 depends_on: [db]
10 db:
11 image: postgres:16-alpine
12 environment:
13 POSTGRES_USER: umami
14 POSTGRES_PASSWORD: strong-pass
15 POSTGRES_DB: umami


3. Pierwszy login. https://stats.example.com, login admin / umami. Pierwsza rzecz: zmień hasło. Druga: Settings → Websites → Add Website (Domain: example.com). Skopiuj Website ID, to UUID w formacie aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee.

4. Wstrzyknij tracker. W head strony:

1<script defer
2 src="https://stats.example.com/ascii.js"
3 data-website-id="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
4 data-do-not-track="true">
5</script>


data-do-not-track="true" szanuje browser DNT, czyli GDPR data-minimization za darmo.

Pułapki, na które się nadziejesz

CSP. Jeśli masz strict Content-Security-Policy (a powinieneś), tracker padnie cicho. Domena Umami musi wejść do dwóch dyrektyw:

1script-src 'self' https://stats.example.com
2connect-src 'self' https://stats.example.com


Pierwsza ładuje skrypt, druga puszcza POST /api/send z eventami.

Adblock. uBlock Origin i EasyPrivacy blokują domyślną ścieżkę /script.js. Dlatego w configu jest TRACKER_SCRIPT_NAME=ascii.js. Własna nazwa plus first-party domena (stats.example.com, nie cloud.umami.is) zbija wykrywalność do minimum. Jak dalej coś filtruje, zmień nazwę na cokolwiek genericznego (t.js, events.js).

Coolify-specific gotcha. Jeżeli stawiasz przez Coolify, pole Domains dla services wymaga formatu https://stats.example.com:3000. Port :3000 to wskaźnik dla Coolify który wewnętrzny port mapować, a nie publiczny. Traefik i tak wystawi to na 443. Counter-intuitive, ale tylko w Coolify; w czystym Docker Compose ten problem nie istnieje.

Co dostajesz

Czysty dashboard z page-viewami, top pages, referrers, kraje, urządzenia. Bez cookie bannera, bez ekranu zgód, bez fingerprint trackera, bez przekazywania danych do Google'a. Mała strona spokojnie zmieści się w 200 MB RAM-u, taniej niż jeden płatny GA4 add-on.

I to jest cała magia self-hosted analytics w 2026: jeden kontener, jedna subdomena, pięć minut roboty. Reszta to już Twój ruch.