levr/content/posts/installation-goaccess-nixos.en.md
2025-10-08 22:47:32 +02:00

2.5 KiB
Raw Blame History

+++ date = '2025-10-06T01:00:51+02:00' title = 'Goaccess install on NixOs' tags = [ "nixos","statistics" ] +++

When you self-host, there comes a moment when curiosity takes over. Who visits my site? At what time? Which pages actually get attention?

And, sooner or later, the big question: Is anyone, anywhere, really reading what I write?

You could go for Google Analytics or Matomo — but that comes with its own baggage:

  • Questionable privacy practices,
  • Third-party scripts injected into your site,
  • Extra weight on every page,
  • And a dependency on services I dont want.

GoAccess, on the other hand, takes the minimalist route. No scripts, no tracking, no cookie banner — it reads directly from the web server logs (in my case, Caddy) and generates a clear, efficient report.

Thats the kind of philosophy I like:

  • Privacy-friendly — nothing leaves my server, no profiling.
  • Simple — I dont touch my site, I just process the logs.
  • Fast — one command and I instantly see who visited and how.
  • Self-hosting friendly — no database, no bloated setup.

In short, GoAccess fits perfectly with what I look for in my homelab:

→ Getting visibility into my services without turning it into a Rube Goldberg machine.

Minimal setup on NixOS

GoAccess is available directly from the official repository. I just added the package and a small systemd service to generate a static report every hour:

{pkgs, ...}: {
  environment.systemPackages = with pkgs; [
    goaccess
  ];

  # Service for generation the GoAccess static report
  systemd.services.goaccess-report = {
    description = "Generate GoAccess HTML report";
    serviceConfig = {
      ExecStart = "${pkgs.goaccess}/bin/goaccess /var/log/caddy/access-levr.porzh.me.log --log-format=CADDY -o /var/www/goaccess/index.html";
    };
  };

  # Timer to regénérate the report every hour
  systemd.timers.goaccess-report = {
    description = "Hourly GoAccess report generation";
    wantedBy = ["timers.target"];
    timerConfig = {
      OnCalendar = "hourly";
      Persistent = true;
    };
  };
  services.caddy = {
    virtualHosts = {
      "koum.porzh.me" = {
        extraConfig = ''
          root * /var/www/goaccess
          file_server browse
          try_files {path} {path}/ /index.html

        '';
      };
    };
  };
}

Whats next?

In the second part, Ill go a bit further:

  • filtering out local network IPs,
  • generating daily, weekly, and monthly reports,
  • and explaining why I chose not to use the “live” mode.

Because yes — even analytics deserve a bit of restraint.