Skip to content

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)