Using trckable

Self-hosted web analytics: set it up in one container

Self-hosting analytics means the visit data lives on a server you run. With trckable that is one Docker image and one volume. Here are the real steps, and the settings that bite if you skip them.

Albi4 min read

In short

  • Self-hosted analytics runs on your own machine, so the visit data never goes to a third party.
  • trckable is free and open source (AGPL-3.0), every feature included. It is one container and one volume, with no external database.
  • You need 1 vCPU, about 64 MB of memory when idle and 1 GB of disk to start. The image is about 31 MB compressed.
  • The two settings that matter most are TRCKABLE_BASE_URL and, behind a reverse proxy, TRCKABLE_TRUST_PROXY.

Why self-host

Three reasons come up again and again. Ownership: the data is in a volume you can back up, copy or delete. Location: you pick the country and the provider. Cost shape: the price is a small server, not a price per pageview.

The cost is yours too: you do the updates, the backups and the monitoring. If you would rather not, trckable Cloud runs the same product with a 14-day free trial. The software is the same either way.

What it needs

ResourceMinimum
CPU1 vCPU
Memory64 MB idle; allow about 350 MB for heavy reports
Disk1 GB to start, about 44 bytes per event
DatabaseNone: DuckDB and SQLite are embedded
PortsOne

These come from the self-hosting docs, whose figures are measured in CI.

Run the container

docker run -d --name trckable \
  -p 8080:8080 \
  -v trckable-data:/data \
  -e TRCKABLE_BASE_URL=https://stats.example.com \
  --restart unless-stopped \
  ghcr.io/trckable/trckable:latest

The volume holds everything: both databases, the write-ahead log, the geolocation file, the encryption key and the backups. Never run without it, or each restart starts empty. The image runs as an unprivileged user and works on x86-64 and arm64.

Prefer Compose? The repository carries a compose.yml, and there are templates for Railway, Coolify, Dokploy, Umbrel and a Helm chart in the deploy folder. Whichever you use, set a stop grace period of 30 seconds: on shutdown the server drains what it has and writes it to disk, so a redeploy does not drop events.

Put HTTPS in front

Run it behind Caddy, nginx or Traefik on a domain such as stats.example.com. Two things to get right:

  • Tell it about the proxy. trckable needs the visitor's address to work out a country, and it must not believe a forged one. Set TRCKABLE_TRUST_PROXY to match your setup: header:X-Real-IP if your own proxy sets that header, xff if a CDN you trust appends to X-Forwarded-For. It reads the rightmost entry, never the leftmost. Get it wrong and every visit seems to come from your proxy; the Health tab says so.
  • Do not buffer the live feed. It is a long-lived response. In nginx:
location /api/v1/ {
  proxy_pass http://127.0.0.1:8080;
  proxy_buffering off;
  proxy_read_timeout 1h;
  proxy_set_header X-Real-IP $remote_addr;
}

Add the script to your site

Add the site in the dashboard, then copy the tag from Settings, Install into your <head>:

<script
  defer
  data-site="tkb_a1b2c3d4"
  data-domain="example.com"
  src="https://stats.example.com/js/tkb_a1b2c3d4.js">
</script>

For React and Next.js there is an npm package instead, npm i trckable; see Next.js without cookies. There is also a WordPress plugin. Then check the install: npx trckable doctor --host https://stats.example.com --url https://example.com only reads and tells you what is wrong.

Back it up, and keep it updated

A daily backup is made on the volume, and you can copy each one to an S3-compatible bucket with TRCKABLE_BACKUP_S3. Test a restore once: the backup guide shows how. To upgrade, pin a release tag instead of latest, change it and redeploy; a migration that changes the store takes a copy of it first (upgrading).

Settings, Health answers "is it working" without reading logs: version, uptime, events accepted and dropped, disk and how long it lasts, and the last backup.

What to watch

  • One writer. One instance per volume, never two replicas.
  • Disk. When it is full, new visits are refused and Health shows the error. The server retries by itself every 30 seconds.
  • Privacy still applies. Hosting it yourself does not change what a visitor's browser does. Cookieless mode, retention and the privacy paragraph are all in Settings, Data & privacy. See what cookieless analytics is.
  • Outbound calls are opt-in. The server does not phone home. Optional features such as Mentions and Answers are off until you enable them, and send only what the docs say they do.

Questions

Do I need a database server?
No. DuckDB and SQLite are embedded, and everything lives in the data volume.
Can I run it on a Raspberry Pi or a small VPS?
The image works on x86-64 and arm64. The minimum is one vCPU and about 64 MB of idle memory, so a small VPS is enough for a modest site; heavier reports need more headroom.
Is it really free?
The software is, under the AGPL-3.0, with every feature. You pay for your server. The licence asks that if you change the server and offer it as a network service, you share your changes.

The newsletter

Notes on measuring what matters.

Occasional emails from Albi: new posts, one chart worth reading, what changed in trckable.

We send one email to confirm your address, and nothing more until you confirm. Every newsletter has a link to leave. What we keep, and who sends it: privacy.

Keep reading

Counted, never watched.