ADR 0003 — Self-hosted Umami for analytics
ADR 0003 — Self-hosted Umami for analytics #
Status: Accepted · Date: 2024-01 (recorded retroactively 2026-04)
Context #
We wanted basic analytics: page views, referrers, country-level geographies, and conversion tracking on the contact form. Constraints:
- GDPR-clean without a cookie consent banner (banners hurt UX and bias data)
- No data sold to or shared with ad networks
- Aligned with the open-source brand we sell to clients
- Cheap or free at our traffic levels
Options considered: GA4, Plausible (cloud), Plausible (self-hosted), PostHog (cloud), Umami (self-hosted).
Decision #
Umami, self-hosted on the existing Coolify instance.
We rejected:
- GA4. Requires a cookie banner under strict-reading EU rules; data flows into Google’s advertising stack. Both points are misaligned with the brand.
- Plausible cloud. Good product, but a recurring subscription where we already run Coolify capacity at zero marginal cost.
- Plausible self-hosted. Comparable to Umami but with a heavier footprint and slightly stricter licence stance for commercial self-hosting.
- PostHog. Powerful but oversized for a five-page site; product-analytics framing is wrong shape.
We chose Umami because:
- Open source, MIT
- Cookieless tracking — no consent banner required
- Feature parity with Plausible for our needs (referrers, geos, custom events)
- Same Coolify host as the rest of our stack; zero marginal cost
- Custom event API works cleanly for contact-form conversion tracking
Consequences #
- ✅ Zero recurring cost
- ✅ No cookie banner; the site loads exactly as designed for everyone
- ✅ We own the data and can run our own queries
- ✅ Demonstrates the “self-host where it makes sense” principle to anyone curious enough to inspect the page
- ⚠️ Operational cost: backups, upgrades, monitoring of the Umami container are now on us
- ⚠️ Feature roadmap is the Umami project’s, not ours. If they pivot or stagnate, we revisit.
Status #
Accepted, in production. Conversion events are wired to the contact form via the custom event API.