Cookie banners are a bad trade. You annoy every visitor to keep a GA property that mostly tells
you what you already knew. If you've been eyeing the self-hosted analytics space — Plausible,
Umami, Swetrix — you've probably noticed they're not interchangeable: Plausible and Umami are
intentionally minimal (pageviews, referrers, a Postgres or SQLite backend, done). Swetrix goes
further — funnels, custom events, error tracking, performance monitoring, session replay, no
consent banner required — and it pays for that with a heavier backend: ClickHouse instead of a
row-store.
That heavier backend is also where the actual self-hosting trap lives, and it's not the one
people expect.
The trap: ClickHouse isn't just your event store, it's your login too
Most people assume the risk of a lightly-configured analytics stack is "I might lose some
traffic history." With Swetrix Community Edition that's not the failure mode. Swetrix has no
second database — initialise_selfhosted.js creates user, project, refresh_token and
salt directly in ClickHouse, and the API reads accounts from the same place. There's no
Postgres quietly holding your login while ClickHouse only holds pageviews.
So a ClickHouse container with no persistent volume doesn't lose your dashboards on the next
deploy — it loses your account. I reproduced this directly: spin up ClickHouse without a mounted
volume, restart the container, and the next query is
SELECT count() FROM analytics.user
Code: 60 ... UNKNOWN_TABLE
Nothing. No first-user prompt, no error banner telling you why — the app just looks freshly
installed. On a platform like Railway, where a redeploy is a completely normal Tuesday (a config
change, a restart, a plan upgrade), an unmounted ClickHouse means your Swetrix instance quietly
resets itself at some point, and you won't know until you try to log in.
The second trap, and I hit it myself first
Swetrix's web app calls its own backend at ${BASE_URL}/backend, so whatever sits in front of
it has to strip that prefix before forwarding to the API. The natural-looking nginx config is:
set $api_upstream http://api:3000;
proxy_pass http://$api_upstream/;
This looks fine and is wrong. The moment the upstream is a variable, nginx skips its usual
location-prefix rewrite and forwards the literal request URI instead. Every /backend/* call
lands on the API's root path, not on the route it's supposed to hit:
POST /backend/v1/auth/register
{"message":"Cannot POST /","statusCode":404}
I know this one's an easy trap because my own first draft of the gateway config had it. Fixed
with an explicit rewrite instead of leaning on the variable-upstream shortcut:
rewrite ^/backend/(.*)$ /$1 break;
proxy_pass http://api:3000;
Register returns 201, login 200.
The tax you're already paying: ClickHouse pinned to one thread
Swetrix's own self-hosting config ships concurrent_threads_soft_limit_num=1, tuned for a 1GB
VPS. If you copy that onto a real container with real vCPUs, every dashboard query serialises
onto a single thread regardless of what you're paying for. I measured the same aggregation query
three times each way on a 4-vCPU container: 3.89s median pinned to one thread vs 2.16s reading
the actual cgroup quota — 1.8x, for free, just by not hardcoding a number meant for a $5 VPS.
Where this leaves you
I maintain a Railway template for Swetrix CE (v5.3.1, pinned) that fixes all three of the above —
ClickHouse and Redis both on persistent volumes, the gateway rewrite, and thread count read from
the container's actual quota instead of a hardcoded 1. Full disclosure: I get a kickback if you
deploy through it.
If you'd rather run it yourself with zero affiliation to me, the fixes above are just as valid on
raw Docker/Compose — mount a volume on ClickHouse's data directory, fix the gateway rewrite, and
set concurrent_threads_soft_limit_num from nproc instead of copying the upstream default.
Upstream repo: https://github.com/Swetrix/swetrix
Where it doesn't fit: if you want the absolute simplest possible self-hosted analytics —
pageviews and referrers, one container, no ClickHouse to think about — Umami or Plausible are
honestly the better call. Swetrix earns its extra weight only if you actually want funnels,
custom events, error tracking or session replay in the same tool. Know which one you're actually
buying before you commit a database engine to it.
Top comments (0)