{
"path": "/3mjagjjhp3s2g",
"site": "at://did:plc:ofrbh253gwicbkc5nktqepol/site.standard.publication/3m3x4bgbsh22k",
"tags": [
"infra",
"website"
],
"$type": "site.standard.document",
"title": "Self-Hosted Analytics with Umami",
"content": {
"$type": "pub.leaflet.content",
"pages": [
{
"id": "019d7db7-6cd4-7997-9188-29fe75bd9a46",
"$type": "pub.leaflet.pages.linearDocument",
"blocks": [
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 85,
"byteStart": 80
},
"features": [
{
"uri": "https://umami.is",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "I added analytics to my sites. Not Google Analytics. Not a third-party tracker. Umami, running on my own infrastructure."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "The honest reason? I just like data. Charts, graphs, patterns – something about visualising information scratches an itch in my brain. I wanted to see what pages get visited, where people come from, what browser they're using. Not for optimisation or growth hacking. Just curiosity."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "For a while I ran ewancroft.uk with zero analytics. The stance was simple: I don't want to be tracked, so why would I track you? But the curious part of me wanted signal. Real data, not guesses from social engagement."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 3,
"plaintext": "What Umami Does Differently"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "The analytics industry normalised invasive tracking. Google Analytics is free because you're the product – your visitors' behavioural data gets folded into cross-site profiles. That's the bargain: give us your visitors' data, we'll give you charts."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Umami breaks that:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.unorderedList",
"children": [
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Self-hosted on my own hardware"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "No cookies"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "GDPR compliant – no cookie banner needed"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Anonymised IPs"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "No cross-site tracking"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 11,
"byteStart": 0
},
"features": [
{
"uri": "https://github.com/umami-software/umami",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Open source (AGPL-3.0)"
}
}
]
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "What's collected: page URLs, referrer, browser, OS, country. That's it. No user IDs, no fingerprinting, nothing to sell."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 3,
"plaintext": "The Setup"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 34,
"byteStart": 21
},
"features": [
{
"uri": "https://www.dell.com/en-us/shop/dell-laptops/inspiron-15-3501-laptop/spd/inspiron-15-3501-laptop",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 83,
"byteStart": 73
},
"features": [
{
"uri": "https://www.postgresql.org/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 98,
"byteStart": 93
},
"features": [
{
"uri": "https://caddyserver.com/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 150,
"byteStart": 133
},
"features": [
{
"uri": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 241,
"byteStart": 220
},
"features": [
{
"uri": "https://search.nixos.org/options?channel=unstable&query=services.umami",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Umami runs on an old Inspiron 3501 laptop sitting on a shelf in my room. PostgreSQL backend, Caddy reverse proxy, exposed through my Cloudflare Tunnel. The whole thing took about 30 minutes to configure once I found the native nixpkgs module."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 75,
"byteStart": 57
},
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
]
},
{
"index": {
"byteEnd": 112,
"byteStart": 92
},
"features": [
{
"uri": "https://www.postgresql.org/docs/current/auth-peer.html",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "The main gotcha was peer authentication. The module uses DynamicUser = true by default, but PostgreSQL peer auth requires the system username to match the database username. Small override and it was working."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 56,
"byteStart": 44
},
"features": [
{
"uri": "https://ewancroft.uk",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 75,
"byteStart": 58
},
"features": [
{
"uri": "https://docs.ewancroft.uk",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 98,
"byteStart": 77
},
"features": [
{
"uri": "https://malachite.croft.click",
"$type": "pub.leaflet.richtext.facet#link"
}
]
},
{
"index": {
"byteEnd": 194,
"byteStart": 171
},
"features": [
{
"uri": "https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Adding the tracking script to each site – ewancroft.uk, docs.ewancroft.uk, malachite.croft.click – was a single line per domain with a unique UUID. I also updated the Content Security Policy on the main site to allow scripts from my analytics domain."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 3,
"plaintext": "Transparency"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 49,
"byteStart": 27
},
"features": [
{
"uri": "https://analytics.ewancroft.uk",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "The dashboard is public at analytics.ewancroft.uk. Nothing to hide because nothing sensitive is collected. If I were tracking something I didn't want public, that would be a privacy breach for visitors."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 27,
"byteStart": 15
},
"features": [
{
"uri": "https://en.wikipedia.org/wiki/Do_Not_Track",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "Umami respects Do Not Track. Enable it in your browser and nothing gets sent."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 3,
"plaintext": "The Trade-off"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "I'm not claiming this makes my sites perfectly private. Embedded content has its own data practices. But for the analytics layer, the part I control, I've made the most privacy-respecting choice available."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"index": {
"byteEnd": 111,
"byteStart": 97
},
"features": [
{
"uri": "https://www.raspberrypi.com/products/raspberry-pi-5/",
"$type": "pub.leaflet.richtext.facet#link"
}
]
}
],
"plaintext": "The hardware costs nothing – it's repurposed kit that would otherwise gather dust. Moving to a Raspberry Pi 5 soon will drop the power draw even further. No behavioural data. No external parties. Just charts I can stare at when the mood strikes."
}
}
]
}
]
},
"bskyPostRef": {
"cid": "bafyreidogkgkpsnbgl3fldg7asehtappdhzlgbnjexk7cbt47j4q34pdoe",
"uri": "at://did:plc:ofrbh253gwicbkc5nktqepol/app.bsky.feed.post/3mjagjpg4ts2g",
"commit": {
"cid": "bafyreifluz42swvxnfkiolk3srocrvpgucmg7vbloiom6txcdltweho2hi",
"rev": "3mjagjpko4l2q"
},
"validationStatus": "valid"
},
"description": "I can see you, anon.",
"publishedAt": "2026-04-11T18:07:15.928Z"
}