Analytics & Monitoring¶
Self-hosted, privacy-first analytics and infrastructure monitoring.
Stack¶
- Website Analytics: Umami (self-hosted)
- Infrastructure Monitoring: Prometheus + Grafana
- Logs: Loki
- Uptime: Uptime Kuma
Umami Analytics¶
Privacy-focused, GDPR-compliant web analytics. No cookies, self-hosted, ~2KB script.
Collects: - Page views, referrer, country (IP discarded after geolocation) - Device type, browser, OS - Custom events (button clicks, form submissions)
Doesn't collect: - Personal info, IP addresses, cookies, user identifiers
Setup (Docker Compose)¶
version: '3.8'
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
ports: ["3000:3000"]
environment:
DATABASE_URL: postgresql://umami:${DB_PASSWORD}@postgres:5432/umami
APP_SECRET: ${APP_SECRET}
depends_on: [postgres]
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- umami-db:/var/lib/postgresql/data
Installation:
# Generate secrets
openssl rand -base64 32 # DB_PASSWORD
openssl rand -hex 32 # APP_SECRET
# Start
docker compose up -d
# Access: http://vps-ip:3000
# Login: admin/umami (change immediately)
Add to Website¶
<script
async
defer
data-website-id="your-website-id"
src="https://analytics.example.com/script.js"
></script>
Track custom events:
umami.track('contact-form-submit', { service: 'Platform Engineering' });
Cloudflare Tunnel (Secure Access)¶
cloudflared tunnel create umami-analytics
cloudflared tunnel route dns <tunnel-id> analytics.example.com
cat > ~/.cloudflared/config.yml <<EOF
tunnel: <tunnel-id>
ingress:
- hostname: analytics.example.com
service: http://localhost:3000
- service: http_status:404
EOF
sudo cloudflared service install
Monitoring (Prometheus + Grafana)¶
Add to docker-compose.yml:
prometheus:
image: prom/prometheus:latest
volumes: ["./prometheus.yml:/etc/prometheus/prometheus.yml"]
ports: ["9090:9090"]
grafana:
image: grafana/grafana:latest
ports: ["3001:3000"]
node-exporter:
image: prom/node-exporter:latest
ports: ["9100:9100"]
prometheus.yml:
scrape_configs:
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
Backups¶
#!/bin/bash
# Daily backup at 2 AM
docker exec umami-db pg_dump -U umami umami | gzip > /backups/umami-$(date +%Y%m%d).sql.gz
Cost¶
- VPS CPX11: €4.50/month
- Cloudflare Tunnel: Free
- Backups: ~€0.01/month
Total: ~€5/month
vs. Plausible (€9), Fathom ($14), Simple Analytics (€19)