Skip to content

Umami

Umami is the privacy-focused analytics service for the personal site. Its dashboard is private at https://umami.lab.fobiat.dev; it is reachable only from the LAN or through Tailscale.

The service runs the upstream ghcr.io/umami-software/umami:3.3.0 image as a non-root user with a read-only root filesystem. Its PostgreSQL database is a one-instance CloudNativePG cluster in the same namespace, using the local-path storage class and a 10 GiB PVC. Both workloads have explicit requests and limits: Umami requests 100m CPU and 128 MiB memory, and PostgreSQL requests 100m CPU and 256 MiB memory.

Public collection

The browser collector is live at https://insights.fobiat.dev as of 2026-08-17. The cluster accepts only two exact paths through the external Gateway:

  • /script.js, the tracker script
  • /api/send, the event endpoint

The proxied CNAME to the Cloudflare Tunnel and the zone's one free-tier WAF rate limit on /api/send (20 requests per 10 seconds per IP and cf.colo.id, 10-second block, the zone entitlement's maximum rather than the originally designed 60 seconds, Kyle's call) are both live. Confirmed with a real 25-request burst: the first 20 returned Umami's own 400 for an empty payload, and requests 21 through 25 returned Cloudflare's 429. What's still needed to actually collect data: create the website in Umami, and add its generated data-website-id to the personal site's tracker snippet. The site ID is application data, not a cluster secret, and does not belong in this repository.

The gateway path restriction is a safety boundary, not a substitute for Cloudflare's edge controls. See Exposure and ADR 0014 for the complete publication decision.

Backups

umami-pgdump creates one PostgreSQL custom-format dump every day at 03:05 UTC on its own 2 GiB PVC. The job reads the database connection from CNPG's generated app secret, writes as PostgreSQL's UID 26, checks that the archive is readable with pg_restore --list, and retains seven local dumps.

A VolSync ReplicationSource copies that PVC at 04:10 UTC to a separate 5 GiB restic repository PVC. It uses copyMethod: Direct because local-path does not provide Kubernetes VolumeSnapshots. The first VolSync run occurs immediately when the source is created, which can happen before the first scheduled dump. A successful empty-directory skip is expected in that case; the next scheduled sync is the first archive of real data.

Confirmed live on 2026-08-17: the first non-empty sync processed two dump files and saved Restic snapshot 8786c5a6. The repository URI is local:/mnt/repo, matching VolSync's repository-volume mount. Its value and the restic password are SOPS-encrypted Secret data fields so Flux can reconcile updates through server-side apply.

The repository is an interim same-node copy until the R2 Barman plugin and off-node retention are configured. It protects against an accidental PVC deletion, not loss of the host or its data disk. Restore testing is still required before calling this a recovery path.

Checks

Gatus checks the private dashboard at https://umami.lab.fobiat.dev. CloudNativePG's PodMonitor exposes PostgreSQL metrics to the existing Prometheus stack. The dashboard is healthy only when the Umami Deployment, the umami-db Cluster, and its generated app secret are all ready.

The deployment is annotated for Stakater Reloader. A future change to its managed app secret creates a new pod, so the process does not keep an old environment value.