Configuration
Every setting is an environment variable, and none of them is required. trckable starts with sensible defaults and generates its own secrets.
Required
None. That is the point: a fresh container boots, generates its secret, prints a setup link and starts accepting events.
In production you will want two of them anyway:
TRCKABLE_BASE_URL=https://stats.example.com # used in setup links and share links
TRCKABLE_SECRET=... # so the key does not live only on the volumeAll of them
| Variable | Default | |
|---|---|---|
TRCKABLE_DATA_DIR | /data in the container, ./data otherwise | Everything lives here |
TRCKABLE_ADDR | :8080, or :$PORT | Listen address |
TRCKABLE_BASE_URL | Railway's public domain, if any | Used when trckable has to write its own URL |
TRCKABLE_SECRET | generated into data/secret.key | Encrypts provider API keys and backups |
TRCKABLE_SMTP_URL | none | An SMTP server for alerts by email: smtp://user:password@smtp.example.com:587 (STARTTLS) or smtps://…:465. Without it, alerts go to webhooks only |
TRCKABLE_MAIL_FROM | none | The sender address for those emails, e.g. trckable@example.com |
TRCKABLE_BACKUP_S3 | none | A bucket each daily backup is copied to: https://KEY:SECRET@host/bucket/prefix?region=… — see Backups |
TRCKABLE_BACKUP_KEEP_DAYS | 30 | How long copies stay in that bucket. The newest is never removed |
TRCKABLE_OPERATOR_TOKEN | none | Turns on GET /_trckable/usage and GET /_trckable/health for your own monitoring — see below |
TRCKABLE_SETUP_TOKEN | generated and printed once | First-run secret |
TRCKABLE_TRUST_PROXY | auto | auto, none, xff or header:<Name> — see below |
TRCKABLE_GEO | country | country (~4 MB), city (~130 MB) or off |
TRCKABLE_GEO_DIR | none | A shared geo directory that someone else keeps fresh (trckabled geo update <dir> from cron). Many instances on one machine then map one copy, and none of them downloads |
TRCKABLE_UPDATE_CHECK | on | off stops the dashboard telling you about new versions. When on, the owner's browser looks at GitHub's public list of releases at most once a day, sending nothing about the instance; the server itself never calls out. Each person can also turn it off in their browser from Account |
TRCKABLE_DUCKDB_THREADS | 2 | Analytics query threads |
TRCKABLE_DUCKDB_MEMORY | 256MB | Analytics memory cap |
TRCKABLE_DRAIN_SECONDS | 25 | How long to finish work on shutdown |
TRCKABLE_LOG_LEVEL | info | debug, info, warn, error |
TRCKABLE_SITES | — | Domains to create at boot, comma-separated |
TRCKABLE_API_TOKEN | — | A bearer token for automation against /api/v1 |
There are two TRCKABLE_UNSAFE_* variables. They exist for benchmarks, they are
named that way on purpose, and they must never be set in production.
Trusting a proxy
trckable needs the visitor's address to work out a country, and it must not believe a forged one. The address is used in memory and then discarded — it is never written to a log or the database.
| Value | Use when |
|---|---|
auto | The default. Railway's X-Real-IP on Railway, the socket address elsewhere |
none | trckable faces the internet directly |
header:X-Real-IP | Your own nginx, Caddy or Traefik sets a header you control |
xff | A CDN you trust appends to X-Forwarded-For. trckable reads the rightmost entry, never the leftmost |
The leftmost X-Forwarded-For value is whatever the client sent, so it is a
wish, not a fact. trckable never reads it.
If this is wrong, every visit appears to come from one place.
Geolocation
The database comes from DB-IP (CC BY 4.0) and downloads
itself on first start. country is about 4 MB and is the default. city is
about 130 MB and is only worth it if you turn city recording on per site.
off downloads nothing and records no location at all.
Checking what it decided
Settings → Health answers "is it working" without a log. It is shown to everyone who signs in. It shows:
- the version, how long the server has been up, and the events accepted, dropped as bots and rejected since it started;
- how many events are in the write-ahead log but not yet in reports;
- memory: the whole process's resident memory where the server can read it (on Linux), otherwise the Go runtime's own memory only, and it says which;
- disk: what trckable's data uses, what is free on the volume, and how long that lasts at the rate of the last seven days of stored events;
- backups: the last one, or the last failure and why; the off-site copy, or the last failed copy and why;
- payments, once a provider is connected: the last webhook, how many wait to be processed, and the last reconciliation.
When the disk is full, new visits are refused and Health shows the error. The server tries again every 30 seconds by itself, and nothing already stored is harmed.
The same answer is available without signing in, for your own monitoring, with
TRCKABLE_OPERATOR_TOKEN:
curl -H "Authorization: Bearer $TOKEN" https://stats.example.com/_trckable/healthIt also carries "key_on_volume": true while TRCKABLE_SECRET is not set:
the instance key then exists only as data/secret.key, on the same disk as
the backups it unlocks. Without the token set, the endpoint does not exist
(404).
Secrets as files
TRCKABLE_SECRET, TRCKABLE_API_TOKEN, TRCKABLE_SETUP_TOKEN, TRCKABLE_BACKUP_S3 and
TRCKABLE_OPERATOR_TOKEN can also be read from a file: set TRCKABLE_SECRET_FILE=/run/secrets/trckable
instead. That is how Docker and Kubernetes secrets and systemd credentials hand
them over, and a file never shows up in the process environment. A file that is
named but cannot be read stops the server at start — an empty secret would
quietly create a new key.