Own Your Analytics Data: Self-Hosted vs Private EU SaaS in 2026
When self-hosted analytics actually helps, when a private EU SaaS is the right answer, and how Statnive Live ships both with the same privacy invariants.
Self-hosted web analytics is a 2026 reset, not a niche
For most of the last decade, “self-hosted analytics” was a position you took for ideological reasons — a Plausible-or-Matomo flag-plant against the GA4 default. In 2026 it’s something else: the European Commission’s Data Act (applicable since 12 September 2025) explicitly prohibits SaaS vendor lock-in via proprietary storage formats, and there’s a public, citable SaaS exodus of European businesses repatriating workloads from US-controlled cloud platforms — “German Mittelstand manufacturers, French government agencies, Dutch healthcare providers”, in MassiveGRID’s framing. Industry tracking now describes data sovereignty as “a pragmatic response to regulatory pressure, political reality, and the growing recognition that data sovereignty isn’t optional.”
Statnive Live ships in two shapes: a self-hosted Go binary you run yourself, and a private EU SaaS in Nuremberg. This post is the honest version of the question every privacy-minded operator now has to answer: which one actually fits me? Where this post makes a regulatory or product claim, you’ll find a footnoted source — and where the trade-off is real, the trade-off is named.
This is article 3 in a short series introducing Statnive Live. Article 1 walked through the WordPress plugin vs Statnive Live decision; article 2 covered the 2026 EU regulatory landscape. This one is about the deployment shape — controller vs processor, blast radius, and the threat model on each side.
What self-hosting actually protects you from
Self-hosting collapses five common SaaS-analytics threats into one server you already own:
- Vendor going out of business, pivoting, or arbitrarily changing pricing. SmartSaaS and Sharp Hue frame this as the canonical SaaS risk; if your analytics stack lives on someone else’s servers, your historical data lives on someone else’s roadmap.
- Vendor acquisition. New owners can change the privacy posture, raise the price, or force a re-platform. J. Chang Law tracks the contractual side of these disputes.
- Vendor data-breach exposure. When visitor data sits in a multi-tenant SaaS, every breach at the vendor is a breach of your data (Kiteworks framing). Self-hosting caps the blast radius to your own server.
- Cross-border transfer risk. Hosting outside the EEA triggers GDPR Chapter V (Articles 44–49). The cleanest answer is to stay inside the EEA — see Article 2 of this series for the regulatory case.
- Regulator-forced disclosure. The US CLOUD Act + FISA 702 reach any data controlled by a US-incorporated provider, regardless of where it physically sits. As Civo puts it: “Storing your data in Frankfurt or Dublin does not put it beyond the reach of US law enforcement. If the provider is a US company, US authorities can compel access to any data that company controls, wherever it is stored.” FISA 702 was due to expire in April 2026 with renewal pressure already in flight at the time of writing — the post-renewal scope will tell you whether the picture changes; the architectural answer doesn’t depend on it.
A self-hosted binary on operator-controlled infrastructure run by a non-US legal entity collapses all five threats into one problem the operator already manages — their own server.
What self-hosting does not protect you from
The honest companion list. Self-hosting shifts risk; it doesn’t remove it. Four things stay your problem:
Compromise of your own server. Host OS, ClickHouse, the Go binary, the network — all of it now sits inside your blast radius. If your sysadmin posture is weak, self-hosting can make things worse, not better. The Slimstat / TeamUpdraft framing of self-hosted-as-server-load is the same trade-off cast as performance.
Encryption-key loss. LUKS-encrypted disks and age-encrypted backups work as long as the keys exist. Lose them and the data is gone — same as an SSH key or a Bitcoin wallet. We document the LUKS recovery story in docs/luks.md; everything else is your password manager’s problem.
Insider risk on your own team. A malicious or negligent admin can read raw events before hashing, drop tables, or exfiltrate audit logs. RBAC (admin / viewer / API-only) helps compartmentalise; the residual risk is yours.
Operator error. Deleting data, mis-configuring backups, leaving the dashboard wide-open. The same restore-on-release drill that defends against a breach defends against operator error — but only if you’ve actually run it.
If those four still sit better with you than the SaaS-vendor list above, self-host. If not, the private EU SaaS path covers most of them while keeping the EU-only data residency story intact.
The single-binary architecture
Both paths run the identical Go binary against the identical ClickHouse schema. There is no fork, no second codebase. All assets — tracker JS, dashboard SPA, ClickHouse migrations, vendored Go dependencies — are embedded via go:embed. No external CDN, no runtime npm install, no go mod download at runtime.
Air-gap is a non-negotiable project goal, not an aspiration. The binary is required to run fully air-gapped under iptables -P OUTPUT DROP with zero required outbound connections. The release gate runs the air-gap test on every release: events ingest, rollups materialise, dashboard renders, all under the iptables drop. Outbound traffic on opt-in features (e.g. an optional GeoIP database update) routes through internal/httpclient/guarded.go — FQDN allow-list, RFC-1918/loopback/CGNAT rejection after DNS resolution, forced HTTPS. The default allow-list is empty.
In CI, the air-gap-validator rule rejects raw *http.Client constructions, runtime DNS/HTTP, CDN imports in the dashboard or tracker, telemetry pings, and external font/script URLs. The contract is enforced by code review and by Semgrep, not by a checklist.
Defence in depth — the concrete claims
The cryptographic and operational primitives, with file paths so you can verify them:
- Visitor identity:
HMAC(master_secret, site_id || YYYY-MM-DD), BLAKE3-keyed, derived in-process, never persisted, rotated daily. Same visitor → different hash each day. Stored asFixedString(16)(BLAKE3-128 truncated to 16 bytes) in ClickHouse. SHA-256 / BLAKE3 are the only hash families anywhere in the binary; no MD5, no SHA-1. - Dashboard auth:
bcryptcost 12 password hashing,crypto/rand32-byte session tokens (256 bits, hex-encoded), 14-day TTL,SameSite=Lax HttpOnly Securecookies. Bcrypt cost 12 takes ~50 ms+ on every machine that ever runs it — that’s the point. - Audit log: JSONL via Go’s
slog, append-only, file sink only in v1,chattr +adiscipline,logrotate copytruncate=off. Syslog and remote sinks deferred to v1.1 — that preserves the air-gap default. - At-rest encryption: LUKS2 with
aes-xts-plain64, key-size 512,argon2idPBKDF,iter-time 2000. Required on shared-tenant cloud VPS (including Netcup VPS 2000 G12 NUE D1); optional on dedicated cage hardware. Measured I/O hit is 40–50% on ext4-over-LUKS1; AES-NI + AVX2 halves it. - Encrypted backups:
clickhouse-backup+age+zstd, cron-scheduled. Restore-test on every release. Backups ship to an EU-only second location for SaaS; for self-host, the location is the operator’s choice. - systemd hardening:
NoNewPrivileges,ProtectSystem=strict,PrivateTmp,CapabilityBoundingSet=CAP_NET_BIND_SERVICE. Service unit ships alongside the air-gap iptables rules viamake airgap-bundle. - Sec-GPC short-circuit: When
Sec-GPC: 1orDNT: 1is set, the request is dropped before the visitor identifier is computed. No pseudonymous identifier is generated for a declining visitor — there’s nothing to delete because nothing was created.
When to self-host vs when to take the private EU SaaS
Five factors actually decide this. Most readers will see themselves in one column quickly.
1. Ops headcount. Self-hosting costs ops time. The operator owns TLS rotation, GeoIP database drops, ClickHouse upgrades, kernel patches, the LUKS passphrase. If you don’t have someone whose job already includes a Linux server, pick the SaaS.
2. Traffic volume. Statnive Live’s design ceiling is 200 M events/day per node on an 8-core / 32 GB box. Below ~10 M events/day either path works equivalently; above ~50 M events/day the operational maturity to self-host starts to look reasonable on its own merits.
3. Audit and compliance posture. Regulated industries often need both a signed Art. 28(3) DPA and infrastructure they direct. The private SaaS path covers the DPA half; self-hosting covers both halves but transfers the operational burden onto your team.
4. Geography. SaaS is processed in Nuremberg, Germany (Netcup VPS 2000 G12 NUE) — EU/EEA-only, no Chapter V transfer. Self-host puts the data wherever your server is. Country-specific data-residency rules (Swiss FADP, German public-sector procurement) sometimes need a specific city — only self-host gives that level of control.
5. Cost at scale. SaaS is traffic-priced (Starter / Growth / Business range from $9–$339/mo across the published tiers; Enterprise above). Self-host trades the SaaS bill for a Hetzner CX43 (~€14/mo) or Netcup VPS 2000 G12 (€25.48/mo monthly) plus operator time. At low traffic SaaS wins on TCO; at very high traffic self-host wins.
If three or more of those tilt the same way, that’s your answer. If they’re split, default to the SaaS for the first 6 months — you can move to self-host later without losing data, because the binary and the schema are the same.
The “private SaaS” middle ground
A precise note on what “private” means in this context. The Statnive Live SaaS is logically tenant-isolated — every event row carries a site_id, every dashboard query routes through whereTimeAndTenant() with WHERE site_id = ? as the first predicate, and a CI tenancy choke-point rule rejects any new query that bypasses it. It is not physical single-tenancy: one ClickHouse cluster serves all customers. Customers wanting physical single-tenancy take the self-host path.
What the SaaS does ship out of the box:
- Art. 28(3) DPA on every plan (including Free), signed dated 2026-04-24. The DPA covers all eight Art. 28(3) sub-paragraphs — instructions only, confidentiality, Art. 32 security, sub-processor authorisation, data-subject-rights assistance, controller-obligation assistance for Arts. 32–36, deletion or return on termination, and audit rights.
- Sub-processor list updated within 7 days of any upstream change, with 14 days’ advance notice to customers via the privacy page; customer may object in writing within that window.
- 30-day customer-export window on termination — full CSV/JSON via the standard export endpoint. After 30 days, raw tables, rollup tables, backups (next backup cycle ≤ 24h), and audit logs are deleted, except where Union or Member State law requires retention.
- 48-hour breach-notification SLA from awareness, mirroring GDPR Art. 33.
- EU/EEA-only processing. No Chapter V transfer of stored personal data outside the EEA. The two US-resident sub-processors that do appear in the chain — Cloudflare DNS-only, Let’s Encrypt for DV certificates — are disclosed in the DPA’s § 6 under DPF adequacy. They receive DNS metadata and certificate metadata only, never application payload.
That last bullet is the load-bearing answer to the CLOUD Act / FISA 702 question. “No US-incorporated party in the processing chain” is the strict version, and it’s what Civo and SoftwareSeni argue for in the sources above. “Saying Frankfurt or Dublin does not make a US provider sovereign” is the thing the architecture has to actually answer; “operated by a German-resident customer of a German legal entity on infrastructure in Nuremberg” is the answer.
Migration path between the two
This part is short because it’s deliberately uneventful.
Same binary, same schema. The migrations live in clickhouse/migrations/ and use {{if .Cluster}} Go-templates from day one — the single-node → Distributed transition is a config flip, not a re-platform.
No data lock-in. ClickHouse uses standard binary formats (Parquet / Native / RowBinary) plus the clickhouse-backup archive format. Migration off Statnive Live into ClickHouse Cloud or a self-managed cluster is a clickhouse-backup restore, not a re-import.
Visitor-scoped export and erasure. GET /api/privacy/access returns the cookie-bound rows on the resolved site (Art. 15); POST /api/privacy/erase is the matching erasure endpoint (Art. 17). The erase path enumerates system.columns dynamically, so any new table that carries a cookie_id is automatically in scope — and the WHERE clause is cookie_id = ? AND site_id = ? so an erase request on one site can never reach another tenant’s rows. DSAR on day one, not retrofitted.
If you start on the private SaaS and later want to self-host, ask for the binary plus a clickhouse-backup archive of your data. The release pipeline produces a reproducible statnive-live-<VERSION>-linux-amd64-airgap.tar.gz plus SHA256SUMS (and an optional Ed25519 signature) — you copy the tarball, clickhouse-backup restore your data, and you’re running the same dashboard against the same schema, just on your hardware.
Common questions
What happens if Statnive Live shuts down?
Same binary, you keep running it. Self-host customers already have it. SaaS customers can request the binary plus a clickhouse-backup archive of their data on the way out. The release pipeline produces a reproducible airgap bundle with SHA256 sums; nothing about that depends on Statnive being a going concern.
Can I move my data to ClickHouse Cloud?
Yes. The data is in standard ClickHouse format; clickhouse-backup restore against a ClickHouse Cloud target is the supported path. If you want to use the data with another tool entirely, the export endpoint produces CSV/JSON.
Can my CI run against a Statnive instance?
Yes. make ci-local brings ClickHouse up, brings the Go binary up, runs integration tests, and tears it all down. Every PR’s CI runner already does this — it’s the same drill you’d use locally.
Where do I keep encrypted backups?
Operator’s choice on self-host: any S3-compatible store, any remote disk, any tape. For SaaS, an EU-only second location is the contractual answer. Restore is tested on every release regardless of where backups land.
CLOUD Act / FISA 702 — does Nuremberg help?
Yes, when the provider is also non-US-controlled. Statnive Live SaaS is operated by a German-resident customer of Netcup (a German legal entity) on Netcup infrastructure in Nuremberg. No US-incorporated party in the processing chain → no CLOUD Act / FISA 702 reach over application payload. Two US-resident sub-processors appear under DPF adequacy — Cloudflare DNS-only and Let’s Encrypt — receiving DNS metadata and certificate metadata only. That’s a different surface than “the database lives on AWS-Frankfurt”.
What about disaster recovery?
The same restore-test runs on every release, against the same encrypted-backup format — age-encrypted, zstd-compressed, clickhouse-backup archive. We’ve also documented the LUKS recovery path in docs/luks.md for the at-rest-encryption layer. There is no magic; there is a drill that has actually been run.
The bottom line
Self-hosting is not a flag-plant in 2026 — it’s the cleanest answer to a measurable trend in EU regulatory and procurement pressure. Statnive Live ships the same Go binary plus the same ClickHouse schema in two shapes: self-hosted, where you take all the operational responsibility and lose the SaaS-vendor risks; and private EU SaaS in Nuremberg, where you take an Art. 28(3) DPA, EU-only data residency, and a 7-day sub-processor SLA — but also the multi-tenant blast radius the architecture was designed to bound.
Pick the path that matches your ops headcount, your traffic, and your audit posture. If you’re undecided, start on the SaaS — the migration to self-host is a clickhouse-backup restore away.
Statnive Live is coming soon at statnive.com/live. Until then, the WordPress plugin is on WordPress.org, the comparison piece walks through the WP-vs-Live decision tree, the GDPR guide covers the regulatory side, and the pricing page lays out both products together. If you’re specifically comparing alternatives to GA, the ranked roundup covers seven privacy-first replacements with WordPress integration notes. If a fact in this post turns out to be wrong, write to me — every claim has a footnote, and we’d rather correct one than ship a polished half-truth.