How visits to OpenAQW's sites are counted, with Umami on Railway
Find a file
2026-10-14 18:47:43 +00:00
LICENSE Umami setup for OpenAQW 2026-10-14 17:13:22 +00:00
postgres.env Umami setup for OpenAQW 2026-10-14 17:13:22 +00:00
README.md Link aq-closet at code.openaqw.com 2026-10-14 18:47:43 +00:00
setup.sql Umami setup for OpenAQW 2026-10-14 17:13:22 +00:00
umami.env Umami setup for OpenAQW 2026-10-14 17:13:22 +00:00

OpenAQW Analytics

This is how visits to OpenAQW's sites are counted. It is Umami, an open-source analytics tool that sets no cookies and stores no addresses, running from Umami's official container image on Railway.

OpenNeo's openneo-analytics does the same job with Plausible. Umami was picked here because it needs only Postgres, where Plausible also needs ClickHouse, which costs several times as much to run on Railway.

What runs

  • Umami: the image ghcr.io/umami-software/umami:3.4.0, limited to 1 vCPU and 1 GB of memory, on port 3000 with its health check at /api/heartbeat. It is at analytics.openaqw.com.
  • Umami DB: Postgres 18 (Railway's postgres-ssl:18 image) with a 5 GB volume at /var/lib/postgresql/data.
  • SALT_ROTATION=day: the hash that tells one visitor from another changes every day, so nobody can be followed from one day to the next.
  • Umami's telemetry and update checks are off.

How a site sends to it

Visitors' browsers never talk to Umami. AQ Closet serves the tracker from its own address and passes each page view and button press on itself (server/stats.js in aq-closet), over Railway's private network to http://umami.railway.internal:3000. On the way it cuts each one down to:

  • the page's path, with no query or fragment (an outfit lives in the query);
  • the referring page, without its query;
  • the page title, screen size and language, and the browser's description, which Umami turns into browser, system and device;
  • the address shortened to its first 24 bits (IPv4) or 48 bits (IPv6), and the country Cloudflare reports.

When the browser sends Do Not Track or Global Privacy Control, nothing is passed on at all.

Settings

umami.env and postgres.env list the variables on the two Railway services. Secrets are placeholders here and live only in Railway. When a setting changes, change it in Railway and here together.

First start

Umami creates an admin account with the password umami when it first starts. A Railway address is public from the moment it exists, so setup.sql replaced that password and added the AQ Closet site before anyone could sign in. It ran as the Umami service's pre-deploy command, with the SQL (and a real password hash) in UMAMI_SETUP_SQL:

printf "%s" "$UMAMI_SETUP_SQL" | prisma db execute --stdin

It changes the password only while it is still the default, so running it again does nothing.

Upgrading

  1. Read Umami's release notes for every version between ours and the new one. Migrations run when the container starts.
  2. Back up the database volume in Railway.
  3. Change the image tag on the Railway service, and in this README.
  4. Deploy, then check that /api/heartbeat answers and visits still arrive.

Moving it

Nothing here depends on Railway except the ${{...}} references and the .railway.internal host names. The same two images with the same variables run under Docker Compose on any machine; point the sites' UMAMI_URL at the new address.

License

MIT, see LICENSE.