Liwan: a Lightweight Matomo Replacement
A privacy-first, self-hosted analytics tool that dropped in where my Matomo instance used to be. One Rust binary, no database to babysit, and I imported all the old data.
I ran Matomo for years. It does the job, but self-hosting it means a PHP app, a MariaDB instance, cron for the archiving, and the general upkeep that comes with all of that. For a handful of small sites where I just want to know what pages people read and where they came from, it was more moving parts than the question deserved.
So I went looking for something lighter, and found Liwan by explodingcamera. It’s privacy-first web analytics in a single Rust binary: no cookies, no cross-site tracking, no persistent identifiers, and the tracking snippet is one line under 1KB. I liked it enough that I forked it, added the couple of things I needed to actually migrate off Matomo, and it’s been running in Matomo’s place ever since. Huge thanks to explodingcamera for the original, none of this exists without that work.
What it looks like
You get a clean dashboard per project: pageviews over time, referrers, top pages, countries, browsers, the usual breakdown, all updating in real time as visitors come in. Click to enlarge.
Why it replaced Matomo
The thing that sold me is what it doesn’t need. Liwan is one self-contained binary. It does use databases (DuckDB for the analytics data, SQLite for the app state), but both are embedded and bundled into the binary, so there’s no separate server to stand up, no cache layer, no archiving cron to get wrong. You can run it on a cheap VPS, an old Mac mini, or a Raspberry Pi, and it stays out of the way. It’s written in Rust on tokio, so it’s fast and it sips resources.
On privacy it’s stricter than most out of the box. No cookies and no persistent identifiers means no consent banner to bolt on, and the data lives on your server, not someone else’s. Bots and crawlers get filtered by default, so the numbers are closer to real people than what a lot of tools show you.
None of that would matter if I couldn’t bring my history along, which is the part I built.
The bits I added
My fork sits on top of upstream and adds the few things I needed for a real migration:
- Matomo import.
liwan import matomopulls your historical pageviews across, month by month, so you don’t start from an empty dashboard. It’s incremental and safe to re-run: a per-site watermark tracks how far it got, and a crash replays cleanly instead of duplicating rows. You map each Matomo site to a Liwan entity explicitly, it retries on rate limits, and there’s a--drop-local-urlsflag to skip thelocalhostnoise from local development. I imported everything from my old instance in one pass. - OIDC single sign-on. Password login still works, but with an
[oidc]section set you get a “Sign in with SSO” button and accounts provision automatically on first login. It matches users on the provider’s subject, so an email or name change never breaks the link, and there are options to restrict who can register. - A couple of extra dimensions. Custom events as a first-class thing you can scope reports by, and an entity dimension to break a project down per tracked site.
The Matomo import and OIDC are the two that made this a drop-in rather than a fresh start.
Not just for Matomo
If you’re coming from Google Analytics, the pitch is the same, only more so. You swap a third-party script that phones home for a one-line snippet pointing at your own server, and the analytics stop being someone else’s product. Liwan doesn’t try to be a marketing suite; it answers “who visited, what did they read, where did they come from” and stops there. For most sites, that’s the whole question.
It won’t replace Matomo for everyone. If you lean on goals, funnels, e-commerce tracking, or Matomo’s heavier reporting, those aren’t here, and that’s fine: run the real thing. For the common case of wanting honest, private, low-maintenance numbers, Liwan is the trade I wanted.
How it stacks up
This isn’t a feature-count contest, and if it were, Liwan would lose it. Matomo, GA, Plausible, and the rest all do far more. The honest point is that for a lot of sites you don’t need most of it, and the features that actually matter to me are “can I self-host it, does it respect visitors, and how much is there to run.” On those, the picture flips:
| Tool | Self-host | What you run | Cookieless | Open source | Cost |
|---|---|---|---|---|---|
| Liwan | Yes | Single binary (embedded DuckDB + SQLite) | Yes | Yes (Apache-2.0) | Free |
| GoatCounter | Yes (or free hosted) | Single Go binary (SQLite or PostgreSQL) | Yes | Yes (EUPL-1.2) | Free |
| Umami | Yes (or cloud) | Node app + PostgreSQL/MySQL | Yes | Yes (MIT) | Free self-host / paid cloud |
| Plausible | Yes (CE) or cloud | Elixir app + PostgreSQL + ClickHouse | Yes | Yes (AGPL-3.0) | Free self-host / paid cloud |
| Matomo | Yes or cloud | PHP + MySQL/MariaDB | Optional (configurable) | Yes (GPL-3.0) | Free self-host / paid cloud |
| Google Analytics | No | Nothing, it’s their servers | No (cookies + consent) | No | “Free” (you’re the product) |
The lightweight, self-host-friendly end is more crowded than it used to be, which is a good thing. What pulled me to Liwan specifically was three things: it’s written in Rust, it has proper multi-site support, and the dashboard is genuinely nice to look at. That combination is rarer than you’d think.
Running it
It’s a single binary (or a Docker image). It reads one TOML config, looks for ./liwan.config.toml or ~/.config/liwan/config.toml, and any value can be overridden with a LIWAN_* environment variable, which is how you keep secrets out of the file. Point your reverse proxy at it, drop the snippet on your pages, and you’re collecting.
The full setup, the OIDC configuration, and the Matomo import walkthrough live in the README, so I don’t have to keep two copies in sync.
Where to get it
Upstream is at liwan.dev and github.com/explodingcamera/liwan. My fork, with the Matomo import and OIDC, is at github.com/franzos/liwan. There’s a live demo on demo.liwan.dev if you want to click around before installing anything.
That’s the shape I was after: a tool that answers the analytics question honestly, keeps the data on my server, and is boring to run. One binary, one config file, my history carried over, and no database server to keep alive.