HitKeep: datenschutzfreundliche Web-Analytics selbst hosten
HitKeep mit Docker und Traefik aufsetzen: cookielose, DSGVO-freundliche Website-Statistik auf dem eigenen Server – mit Tracking-Code und Backups.
Inhaltsverzeichnis
Google Analytics kennt jeder – und genau das ist das Problem: Es schickt die Daten deiner Besucher zu Google, verlangt einen Cookie-Banner und macht dich erklärungspflichtig. HitKeep dreht das um: cookielose Statistik auf deinem Server, unter deiner Domain, ohne dass je ein Byte an Dritte geht.
Was bauen wir?
Am Ende läuft HitKeep 2.12.0 als einzelner Container hinter deinem Traefik, erreichbar
unter https://DEINE_DOMAIN. Du bekommst ein Dashboard mit Seitenaufrufen, Besuchern,
Verweildauer, Referrern und Geräten – gespeist von einem winzigen JavaScript-Snippet, das du
in deine Website einbaust. HitKeep arbeitet cookielos (kein Consent-Banner nötig) und
speichert alles lokal in einer eingebetteten DuckDB-Datenbank. Das Image ist ein schlankes
Distroless-Image (rund 71 MB), das komplett ohne externe Datenbank auskommt – ideal für einen
kleinen VPS.
Hinweis
Voraussetzungen
- Ein Server mit Debian 13 und laufendem Docker (getestet auf einem netcup VPS).
- Ein Reverse Proxy mit Traefik, der TLS-Zertifikate
per Let’s Encrypt holt. HitKeep bringt keinen eigenen HTTPS-Server mit – Traefik übernimmt
die Verschlüsselung. Dieses Tutorial setzt das dort beschriebene
proxy-Netz und den Resolverlevoraus. - Eine (Sub-)Domain, die auf deinen Server zeigt (A-/AAAA-Record). Wir nutzen im Beispiel
DEINE_DOMAIN. - Die Website, die du messen willst – HitKeep misst jede Seite, in die du das Snippet einbaust.
VPS 1000 G12
4 vCore · 8 GB RAM · 256 GB NVMe
ab 10,36 €/Monat
Ein einzelner Container mit eingebetteter DuckDB – der kleinste vServer reicht dafür locker.
💶 5 € Gutschein für netcup-Neukunden:36nc17844976032
(nur Neukunden, keine Domains)
Schritt für Schritt
Schritt 1: JWT-Secret erzeugen
HitKeep signiert die Login-Sitzungen mit einem geheimen Schlüssel. Erzeuge dir einen zufälligen 32-Byte-Wert – nicht ausdenken, sondern generieren:
openssl rand -hex 32Du bekommst eine 64 Zeichen lange Hex-Zeichenkette. Kopiere sie – sie kommt gleich in die Konfiguration. Ändert sich dieses Secret später, werden alle offenen Logins ungültig; halte es also stabil und geheim.
Schritt 2: Compose-Datei anlegen
Lege einen Ordner für den Stack an und wechsle hinein:
mkdir -p /opt/hitkeep && cd /opt/hitkeepErstelle die Datei compose.yaml. Ersetze DEINE_DOMAIN durch deine echte Domain und
DEIN_JWT_SECRET durch den Wert aus Schritt 1:
services:
hitkeep:
image: pascalebeier/hitkeep:2.12.0
container_name: hitkeep
restart: unless-stopped
environment:
HITKEEP_PUBLIC_URL: https://DEINE_DOMAIN
HITKEEP_JWT_SECRET: DEIN_JWT_SECRET
HITKEEP_TRUSTED_PROXIES: 172.16.0.0/12
HITKEEP_DB_PATH: /var/lib/hitkeep/data/hitkeep.db
HITKEEP_DATA_PATH: /var/lib/hitkeep/data
HITKEEP_ARCHIVE_PATH: /var/lib/hitkeep/archive
HITKEEP_BACKUP_PATH: /var/lib/hitkeep/backups
volumes:
- hitkeep_data:/var/lib/hitkeep/data
- hitkeep_archive:/var/lib/hitkeep/archive
- hitkeep_backups:/var/lib/hitkeep/backups
labels:
- "traefik.enable=true"
- "traefik.http.routers.hitkeep.rule=Host(`DEINE_DOMAIN`)"
- "traefik.http.routers.hitkeep.entrypoints=websecure"
- "traefik.http.routers.hitkeep.tls.certresolver=le"
- "traefik.http.services.hitkeep.loadbalancer.server.port=8080"
networks: [proxy]
volumes:
hitkeep_data: {}
hitkeep_archive: {}
hitkeep_backups: {}
networks:
proxy:
external: trueDie wichtigsten Stellen im Detail:
HITKEEP_PUBLIC_URList die öffentliche Adresse, unter der HitKeep erreichbar ist. Daraus baut die Oberfläche später den Tracking-Snippet und die Links. Sie muss exakt der URL entsprechen, unter der du HitKeep aufrufst – sonst kommst du in eine Login-Schleife.HITKEEP_TRUSTED_PROXIESist der Knackpunkt hinter einem Reverse Proxy: Ohne diesen Wert sieht HitKeep als Absender jedes Aufrufs nur die interne IP von Traefik – dann landen alle Besucher als ein einziger.172.16.0.0/12deckt die Docker-Netze ab, sodass HitKeep demX-Forwarded-For-Header von Traefik vertraut und die echte Besucher-IP auswertet.loadbalancer.server.port=8080sagt Traefik, dass HitKeep intern auf Port 8080 lauscht. Der Container veröffentlicht selbst keine Ports nach außen – der Zugriff läuft ausschließlich über Traefik.- Die vier
_PATH-Variablen legen Datenbank, Daten, Archiv und Backups unter/var/lib/hitkeep/ab; die passenden Volumes sorgen dafür, dass sie ein Update oder einen Neustart überleben.
Schritt 3: Stack starten und auf TLS warten
Starte den Container:
docker compose up -dPrüfe nach ein paar Sekunden den Status:
docker compose psDu solltest den Container als healthy sehen – HitKeep bringt einen eigenen Healthcheck mit:
NAME IMAGE SERVICE STATUS PORTS
hitkeep pascalebeier/hitkeep:2.12.0 hitkeep Up 18 minutes (healthy) 7946/tcp, 8080/tcpTraefik holt jetzt im Hintergrund das Let’s-Encrypt-Zertifikat für deine Domain. Prüfe von deinem eigenen Rechner aus, dass die Auslieferung des Tracking-Skripts steht:
curl -sI https://DEINE_DOMAIN/hk.jsErwartete Ausgabe (gekürzt) – Status 200 und ein text/javascript-Typ, aggressiv gecacht:
HTTP/2 200
content-type: text/javascript; charset=utf-8
cache-control: public, max-age=31536000, immutableAchtung
404 von Traefik oder eine Zertifikatswarnung, warte eine Minute (Let’s
Encrypt braucht kurz) und prüfe, dass der A-/AAAA-Record deiner Domain wirklich auf den Server
zeigt. Solange das Zertifikat nicht steht, lädt auch das Snippet im Browser nicht.Schritt 4: Admin-Konto anlegen
Rufe https://DEINE_DOMAIN im Browser auf. Beim allerersten Start begrüßt dich HitKeep mit der
Ersteinrichtung. Lege hier dein Administrator-Konto an – Name, E-Mail-Adresse und ein Passwort.
Nimm eine lange Passphrase oder ein per Passwortmanager erzeugtes Zufallspasswort; dieses Konto
sieht sämtliche Statistiken und darf nicht an einem schwachen Passwort hängen:

Tipp
Schritt 5: Website anlegen und Tracking-Code holen
Nach dem Login klickst du links oben bei Sites auf das Plus und legst deine Website an –
als Domain trägst du die Domain der Seite ein, die du messen willst (z. B. DEINE_WEBSITE).
Öffne dann die Site-Einstellungen und wechsle auf den Reiter Tracking. Dort findest du den Live tracking verifier (der auf den ersten Treffer wartet) und die Konfiguration des Tracking-Codes samt Tracker-Host:

Der einzubauende Code besteht aus einer einzigen Zeile. HitKeep braucht keine Site-ID im Snippet – die Zuordnung passiert automatisch über die Domain der Seite, auf der das Skript läuft:
<script async src="https://DEINE_DOMAIN/hk.js"></script>Hinweis
hk.js wird
von deiner HitKeep-Domain (DEINE_DOMAIN) geladen, zugeordnet wird der Treffer aber der Domain
der besuchten Seite (DEINE_WEBSITE). Beide dürfen unterschiedlich sein – die in HitKeep
angelegte Site muss nur zum Hostnamen der besuchten Seite passen.Automatisches Event-Tracking (ausgehende Klicks, Downloads, Formular-Absendungen) ist standardmäßig aktiv. Optional kannst du auf demselben Reiter „Web Vitals" einschalten, um Ladezeiten (LCP, INP, CLS, FCP, TTFB) mitzumessen, oder „Collect DNT" aktivieren, wenn du auch Besucher mit „Do Not Track" zählen willst – aus Datenschutzsicht ist die Voreinstellung (DNT respektieren) die sauberere.
Schritt 6: Tracking-Code in die Website einbauen
Füge die Snippet-Zeile aus Schritt 5 in den <head> deiner Website ein – bei einer statischen
Seite direkt ins HTML-Template, bei einem CMS in den Kopfbereich bzw. ein „Custom HTML"-Feld.
Durch das async-Attribut blockiert das Skript den Seitenaufbau nicht.
Rufe danach eine Seite deiner Website im Browser auf. Der Live tracking verifier aus
Schritt 5 sollte innerhalb weniger Sekunden von „Waiting" auf einen ersten Treffer springen –
das ist die Bestätigung, dass die Kette Website → hk.js → HitKeep steht.
Schritt 7: Das Dashboard prüfen
Sobald Treffer eintrudeln, füllt sich das Dashboard. Unter Dashboard siehst du die Kennzahlen deiner Website – Live-Besucher, Seitenaufrufe, eindeutige Sitzungen, Absprungrate, Verweildauer und den Traffic-Verlauf:

Weiter unten listet Latest Hits die einzelnen Aufrufe mit Pfad, Zeitpunkt, Referrer und Gerät auf – hier siehst du auf einen Blick, über welche Suchmaschinen und Verweise deine Besucher kommen:

Damit steht deine selbst gehostete Statistik: Jeder Aufruf deiner Website landet direkt in deiner eigenen Datenbank, ohne Umweg über Dritte.
Schritt 8: Mehr als nur Seitenaufrufe
Für den Anfang reichen Seitenaufrufe und Referrer – HitKeep kann aber deutlich mehr, und die Bausteine dafür findest du in der linken Navigation. Ein paar, die sich für die meisten Seiten lohnen:
- Goals (Ziele): Definiere ein Ereignis als Ziel – etwa das Absenden eines Kontaktformulars oder einen Klick auf „Kaufen". So misst du nicht nur, wie viele kommen, sondern wie viele das tun, was du willst.
- Funnels (Trichter): Verkette mehrere Schritte (Startseite → Produktseite → Warenkorb) und sieh, an welcher Stelle Besucher abspringen.
- Events: Neben den automatisch erfassten Ereignissen (ausgehende Klicks, Downloads, Formulare) kannst du eigene Ereignisse aus deinem Frontend senden.
- Web Vitals: Schaltest du sie im Tracking-Reiter ein, siehst du echte Ladezeiten deiner Besucher (LCP, INP, CLS) statt synthetischer Laborwerte.
- UTM: Kampagnen-Parameter (
utm_source,utm_medium,utm_campaign) werden ausgewertet – praktisch, um Newsletter- oder Social-Reichweite auseinanderzuhalten. - Email Reports: Lass dir eine Zusammenfassung regelmäßig per E-Mail schicken, statt selbst ins Dashboard schauen zu müssen.
Eine einzelne HitKeep-Instanz verwaltet außerdem beliebig viele Websites: Über das Plus bei Sites legst du weitere an, jede mit eigenem Snippet und eigenem Dashboard. Du brauchst also keinen zweiten Container, wenn du mehrere Projekte messen willst.
Wenn es nicht funktioniert
Der Live-Verifier bleibt auf „Waiting" / keine Treffer im Dashboard.
Prüfe im Browser (Entwicklertools → Netzwerk), ob hk.js überhaupt geladen wird und der
Sende-Request danach mit Status 2xx zurückkommt. Häufigste Ursachen: Das Snippet steckt nicht
im HTML, die in HitKeep angelegte Site-Domain passt nicht zum Hostnamen der besuchten Seite,
oder ein Ad-/Tracking-Blocker filtert den Aufruf. Da du unter deiner eigenen Domain hostest
(First-Party), greifen die meisten Blocker nicht – manche Listen kennen aber den Pfad hk.js.
Alle Besucher kommen scheinbar von einer einzigen IP.
Dann fehlt oder greift HITKEEP_TRUSTED_PROXIES nicht: HitKeep sieht nur Traefiks interne IP.
Stelle sicher, dass die Variable gesetzt ist (172.16.0.0/12 deckt die Docker-Netze ab) und
starte den Stack neu (docker compose up -d). Danach werten Besucherzahlen und Herkunft wieder
die echte Client-IP aus dem X-Forwarded-For-Header aus.
Traefik liefert 404 oder 502.
Ein 404 heißt meist, dass die Router-Regel nicht greift – prüfe, dass Host(...) deine echte
Domain enthält und der Container im proxy-Netz hängt. Ein 502 deutet auf den falschen Port:
HitKeep lauscht intern auf 8080, deshalb muss loadbalancer.server.port=8080 gesetzt sein.
Nach dem Login landest du wieder auf der Anmeldeseite (Login-Schleife).
Das ist fast immer eine Diskrepanz bei HITKEEP_PUBLIC_URL: Der Wert muss exakt der Adresse
entsprechen, über die du HitKeep aufrufst (inklusive https://, ohne abschließenden Slash).
Korrigiere die Variable und starte den Container neu.
Der Container startet nicht bzw. ist nicht healthy.
Sieh in die Logs: docker compose logs -f hitkeep. Ein fehlendes oder leeres
HITKEEP_JWT_SECRET ist eine typische Startbremse – erzeuge eines wie in Schritt 1 und trage
es ein.
Wartung & Backups
Updates: HitKeep bewegt sich in der 2.x-Reihe zügig weiter – schau etwa monatlich in die
Releases. Für ein Update setzt du den neuen
Tag in der compose.yaml (ersetze 2.12.0 durch die neue Version) und ziehst ihn:
docker compose pull && docker compose up -dWeil die Daten in Volumes liegen, bleiben deine Statistiken dabei erhalten. Pinne die Version
bewusst auf einen festen Tag statt latest, damit ein Neustart dir nicht ungeplant eine neue
Hauptversion unterschiebt.
Backups: Deine gesamte Statistik steckt in einer eingebetteten DuckDB-Datei (hitkeep.db
samt Write-Ahead-Log hitkeep.db.wal) unter /var/lib/hitkeep/data. Sichere deshalb nicht eine
Einzeldatei, sondern das komplette Volume hitkeep_data regelmäßig – am saubersten mit
restic. Weil diese Dateien im laufenden Betrieb beschrieben
werden, sicherst du sie am konsistentesten, indem du den Container kurz stoppst
(docker compose stop), das Volume sicherst und wieder startest – oder das von HitKeep unter
HITKEEP_BACKUP_PATH abgelegte Backup mit einbeziehst. Ein Backup, das du nie zurückgespielt
hast, ist nur ein Hoffnungsschimmer: Teste die Wiederherstellung einmal auf einem Testsystem.
Aufräumen: Die Datenbank wächst mit dem Traffic. Behalte die Größe der Volumes im Blick
(docker system df -v) und plane bei viel Traffic genügend Speicher ein.
Feedback per E-Mail: feedback@serverkueche.de
Das könnte dir auch schmecken

Matomo cookielos selbst hosten: Analytics ohne Consent-Banner
Matomo 5.12 mit Docker & Traefik aufsetzen: datenschutzfreundliche Web-Analytics mit IP-Anonymisierung, ganz ohne Google …

Die eigene Website mit Hugo hosten – wie die Serverküche selbst
Statische Website mit Hugo bauen und im Docker-Container hinter Traefik ausliefern – schnell, sicher, ohne Datenbank. …

Forgejo: der eigene Git-Server hinter Traefik
Forgejo mit Docker & Traefik aufsetzen: eigener Git-Server mit HTTPS, Repos per Web-UI, Klonen über HTTPS und SSH – die …