For the longest time (nearly 6 years according to the data export), I have been a user of Simple Analytics and their dead simple web analytics offering. I believe I got access to it via GitHub’s big bundle of freemium services provided to students and they just didn’t bother checking that I had gone to and dropped out of college twice since they first offered me free access until this month.

So while I was rather satisfied with the free ride I had gotten from Simple Analytics till now, having to pay a rather eye-watering 15 USD every month for it was stretching the otherwise $0 USD budget for my blog. The site itself is hosted by Netlify, but I have a small server from netcup which was suitable to take on the load of running a self-hosted analytics server that I could switch to.

Choosing the analytics service

Like the absolute chump I am, my first visit was to awesome-selfhosted’s analytics page and from there, the only options that stuck out were Plausible and Matomo.

I will be honest here, the reason to not choose Matomo was very superficial - I didn’t want to be running PHP and MySQL. Plausible is an Elixir app and thus not much better as far as me understanding the language goes, but I was reasonably more confident about its choice of Clickhouse and Postgres as the backing data stores.

Getting set up

My server runs NixOS, and thankfully there is a module for Plausible already so setup was very simple and all I had to add was this:

services.caddy.virtualHosts = {
  "https://stats.msfjarvis.dev" = let
    p = config.services.plausible.server;
  in
  {
    extraConfig = ''
      reverse_proxy ${p.listenAddress}:${toString p.port}
    '';
  };
};

sops.secrets.plausible-secret = {
  sopsFile = lib.snowfall.fs.get-file "secrets/plausible.yaml";
};
services.plausible = {
  enable = true;
  server = {
    baseUrl = "https://stats.msfjarvis.dev";
    secretKeybaseFile = config.sops.secrets.plausible-secret.path;
  };
};

The only hiccup here was me apparently not knowing what the hell is 64 bytes supposed to mean and failing to fill in a long enough string into the secretKeybaseFile, which is how it came to end with brother-man-please-work-what-the-fuck-man during testing after which I swapped it out for a 20 word passphrase generated by Bitwarden.

Once everything was running, I set up an admin account and replaced the SimpleAnalytics JS snippet on my website with the one Plausible gave me. Notable difference here was a lack of tracking for users who disable JS, so that’s a net improvement to user privacy that I was too naive to think about in 2020.

Migrating existing data from Simple Analytics

To their credit, Simple Analytics made this very easy and the export option is at the top of their settings page. My only complaint is about their range selection feature, I found the calendar widget very un-intuitive and the input field didn’t let me just fill in the date manually.

Once I had the 45 mB CSV in hand, I looked around for someone else’s homework to copy so I didn’t have to write the migration tool myself. I came across this repo which looked like it would do the job but I had no plans to install NodeJS, so I had gpt-4o (via my free, GitHub-provided Copilot license) rewrite it as a single-file Python script that I could hack on more comfortably. That version is available here, with some additional changes described below.

I ran the script and got a handful of CSVs that Plausible was happy to import, after which I realized that the location data was completely missing. Indeed, my hopes of not having to write any code were going to remain so.

I pulled up the Plausible CSV import docs, got the headers it was expecting for the location CSV and was able to piece together the data relatively easily. One big omission was Simple Analytics not recording cities for page visits, which Plausible had a field for. After figuring out what it was expecting, I filled in zeroes for that column.

I have a big gripe with Plausible’s non-existent error reporting here. When my newly generated CSV failed to import, Plausible simply put up an ⚠️ sign in the list of imports and offered no logs. After digging through their GitHub issues I discovered that the errors had to be pulled out from a Postgres table which I was not very impressed by. For others who come by here with the same problem: it’s in the errors column in the oban_jobs table of the plausible database.

The final step was making the data public like it was on Simple Analytics, so my humble blog’s popularity or lack thereof can continue to be scrutinized by anyone.

Wrapping up

All things considered this took me approximately an hour to get done, most of which was spent digging through Plausible’s GitHub issues and re-learning how to use the psql CLI. I’m quite pleased with how it turned out in the end!

Thanks to Tanvi and Abhinav for answering some questions regarding Plausible.