Star Whale Analytics Lab — Visitor Statistics with Umami
Self-hosting build series part 5. Taking data sovereignty to the mini-PC.
TL;DR
- Google Analytics free = I give visitor data to Google servers + forced cookie consent pop-ups
- Umami self-hosted = own analysis on mini-PC. No cookies, data owned by me, automatic exemption from GDPR
- Docker Compose in 5 minutes. Automatic injection of one line tracker into WP = done
- Display: visitor count, popular posts, traffic sources, devices, countries, dwell time
1. The Hidden Costs of Google Analytics
| Item | Google Analytics 4 | Umami self-hosted |
|---|---|---|
| Cost | Free (personal use) | Free (open source) |
| Data Ownership | Own mini-PC | |
| Cookie Consent Popup | Required (GDPR/CCPA) | Unnecessary (0 cookies) |
| Page Load Load | ~50KB JS | ~2KB JS |
| User Tracking | Cross-site (linked to Google ads) | Same site only |
| South Korea PIPA Compliance | Complicated | Automatic exemption |
| Setup Complexity | 1 hour+ | 5 minutes |
Google Analytics externalizes the real costs to the user (the visitor). Is it normal for Google to take all that data just because a visitor entered my site? For a personal blog, it is right to operate it ourselves.
2. Structure
When the visitor browser receives the Star Whale page, the script in the head <script defer src="https://analytics.sticknstone.org/script.js" data-website-id="..."> executes → asynchronous request to Umami → Umami logs to Postgres. The page display is not interrupted.
3. Docker Compose
/home/user/umami/docker-compose.yml:
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://umami:${DB_PASSWORD}@db:5432/umami
DATABASE_TYPE: postgresql
APP_SECRET: ${APP_SECRET}
ports:
- "3001:3000"
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- umami_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami"]
interval: 5s
timeout: 5s
retries: 6
volumes:
umami_db:
.env:
APP_SECRET=$(openssl rand -hex 32)
DB_PASSWORD=$(openssl rand -hex 24)
Run:
cd /home/user/umami
docker compose up -d
curl -I http://localhost:3001 # HTTP/1.1 200
Default login: admin / umami. Change the password immediately.
4. Registering Star Whale + Receiving Tracker
Umami dashboard → Websites → Add Website:
| Field | Value |
|---|---|
| Name | Star Whale Logbook |
| Domain | sticknstone.org |
Save → Click site → Edit → Tracking Code:
<script defer src="https://analytics.sticknstone.org/script.js" data-website-id="1892c870-dd4d-4ce7-9fd6-2635f8ef6220"></script>
5. Automatic Injection into WP — mu-plugin
The code is automatically placed, not manually by me for every post.
/var/www/html/wp-content/mu-plugins/umami-tracker.php:
<?php
/**
* Plugin Name: Umami Analytics Tracker
* Description: Injects Umami tracker into front-end pages. Excludes logged-in users.
*/
if (!defined('ABSPATH')) exit;
add_action('wp_head', function () {
if (is_admin()) return;
if (is_user_logged_in()) return; // Exclude my own visits
?>
<script defer src="https://analytics.sticknstone.org/script.js" data-website-id="1892c870-dd4d-4ce7-9fd6-2635f8ef6220"></script>
<?php
}, 100);
Checking is_user_logged_in() = the number of times I’ve previewed my own posts will not be counted in the statistics. Surprisingly important.
6. What is Visible on the Dashboard
| Screen | Data |
|---|---|
| Overview | Daily, weekly, monthly visitors, page views, sessions, dwell time |
| Pages | Ranking of popular posts |
| Referrers | Traffic sources (Google, Twitter, other sites) |
| Browsers | Chrome, Safari, Firefox ratio |
| OS | Windows, Mac, iOS, Android |
| Devices | Desktop, mobile, tablet |
| Countries | Visitors by country |
| Languages | Browser languages |
| Events | Clicks and downloads defined by me |
Automatically updates every 5 seconds. Realtime menu = see who is currently viewing which page.
7. Reason to Handle Domain Linking Together
When the tracker is first received, the URL is http://192.168.0.x:3001/script.js (LAN IP). This causes:
– HTTP → Browser Mixed Content block
– LAN IP → Uninterpretable from external visitor PCs
→ Umami must be activated after connecting the domain (part 9). It needs to be exposed externally as analytics.sticknstone.org to function.
FAQ
Q. Is there a free cloud plan for Umami?
Umami Cloud starts at $9 per month. Self-hosted is unlimited and free. If you have a mini-PC, self-hosted is the answer.
Q. Can I migrate from Google Analytics?
No past data import. It’s a fresh start. However, both tools can be operational concurrently (both trackers can be placed).
Q. Do I need to worry about South Korea’s PIPA (Personal Information Protection Act)?
Umami does not store IP addresses (only hashes), and there are no cookies. It qualifies for PIPA exemption.
Q. Is there a mobile app?
No official app. The mobile web is responsive, so it displays well on phones.
Q. Can I export data as CSV?
CSV and JSON export are available on the dashboard. API is also provided.
Next Part Preview
Part 6 — Domain Fraud: Cloudflare Registrar. Reasons for purchasing from Cloudflare with Gabia and WhoIs (price, management integration, future Tunnel connection).
Summary in One Line
The real cost of Google Analytics is the externalization of user data. Umami self-hosted = 5 minutes of Docker on the mini-PC + automatic WP mu-plugin injection = data sovereignty + no cookies + the same information.
