Installation
TravStats ships as a single Docker image. The recommended deployment
is the bundled docker-compose.prod.yml — three services on one
private network, one named volume each, no host paths to manage.
What you need
Section titled “What you need”| Requirement | Minimum |
|---|---|
| Docker Engine | 24.x or newer |
| Docker Compose | v2 (built into recent Docker Desktop / Docker CLI) |
| Disk | ~1 GB for the app + database; ~5 GB more if you keep the optional Ollama container |
| RAM | 512 MB without Ollama, ~4 GB with Ollama running gemma3:12b |
| OS | Any Linux host that runs Docker — bare metal, LXC, VM, NAS appliance |
Quick start
Section titled “Quick start”The only variable you have to set is DB_PASSWORD. Everything else
(instance name, user cap, API keys, backup schedule, Ollama model,
SMTP) is configured later in the first-run setup wizard in your
browser.
# 1. Grab the production compose filecurl -O https://raw.githubusercontent.com/Abrechen2/TravStats/Main/docker-compose.prod.yml
# 2. Set a strong database passwordecho "DB_PASSWORD=$(openssl rand -base64 32)" > .env
# 3. Start the stackdocker compose -f docker-compose.prod.yml up -dOpen http://localhost:3000 and continue with the
first-run wizard.
What the stack contains
Section titled “What the stack contains”The compose file defines three services:
travstats-db Postgres 15 + PostGIS — your flights, trips, achievements, settingstravstats-app The TravStats application (Express backend + React frontend baked in)travstats-ollama Local LLM — primary email parser (recommended) and boarding-pass vision tierAll three live on a single bridge network (travstats-network) and
each gets its own named volume:
| Volume | Holds |
|---|---|
travstats-db-data | The Postgres data directory |
travstats-app-data | Auto-generated secrets (JWT/encryption keys), uploads, log files |
travstats-ollama-data | Pulled LLM models — only allocated if you keep the container |
Optional environment variables
Section titled “Optional environment variables”Put these in .env next to DB_PASSWORD if your setup needs them.
Most installations don’t.
| Variable | Default | When to set |
|---|---|---|
APP_PORT | 3000 | Conflicts with another service on port 3000 |
TZ | UTC | Leave alone — schedulers and logs assume UTC, the UI renders local |
COOKIE_SECURE | (auto from X-Forwarded-Proto) | Override only if your reverse proxy doesn’t set X-Forwarded-Proto |
CORS_ORIGIN | (same-origin) | Set when frontend and backend live on different hostnames |
DATABASE_URL | (built from DB_PASSWORD against the bundled db service) | Pointing at an external Postgres instead of the bundled one |
OLLAMA_URL | http://ollama:11434 | Pointing at a Mac/PC running Ollama elsewhere on your LAN |
VERSION | latest | Pinning a specific image tag instead of tracking latest |
Behind a reverse proxy
Section titled “Behind a reverse proxy”TravStats is happy to live behind any reverse proxy that forwards
Host and X-Forwarded-Proto. Confirmed working in the wild:
- Cloudflare Tunnel — install
cloudflaredon the host (or in a sidecar), point a public hostname athttp://travstats-app:80. No ports exposed to the public internet. - Caddy — one-liner site block:
travstats.example.com { reverse_proxy travstats-app:80 }. - nginx / Traefik — set
proxy_set_header X-Forwarded-Proto $scheme;andHost $host. TravStats auto-detectssecurecookies from there. - Tailscale Funnel / Serve — works as long as
X-Forwarded-Proto: httpsarrives.
If you skip a proxy and serve over plain HTTP on your LAN, set
COOKIE_SECURE=false in .env so the JWT cookie isn’t dropped by
the browser.
Disabling Ollama
Section titled “Disabling Ollama”Ollama is the recommended primary email parser — it handles multi-flight bookings reliably across any airline, where regex templates can struggle. The bundled docker-compose ships it on by default. Most users should keep it on.
If you genuinely can’t spare ~4 GB of RAM (e.g. running on a Pi), stop and remove just that service:
docker compose -f docker-compose.prod.yml stop ollamadocker compose -f docker-compose.prod.yml rm -f ollamaThen in TravStats Admin → Settings, set the Ollama URL to empty. Email parsing falls back to regex-only mode:
- Single-leg bookings on the eight built-in carriers (LH, LX, OS, SN, FR, U2, EW, W6) parse fine
- Multi-flight bookings (round trips, connections) are the regex layer’s weak spot — expect the parser to catch only the first leg, or to miss connections entirely
- Airlines outside the built-in eight will need manual entry or a custom user template
If multi-flight reliability matters and 4 GB RAM is too much on the
TravStats host, point OLLAMA_URL at an Ollama running elsewhere
on your LAN (Mac mini, gaming PC, separate Linux box) — see the
Ollama page.
External Postgres
Section titled “External Postgres”Already running Postgres in your homelab? Drop the bundled db
service from the compose file and set DATABASE_URL directly:
DATABASE_URL=postgresql://travstats:<password>@postgres.lan:5432/travstatsThe database role you connect with needs CREATE on its own schema;
TravStats applies migrations automatically on every container start.
PostGIS is required (CREATE EXTENSION postgis) — TravStats uses
spatial types for some statistics queries.
Quick-start sanity check
Section titled “Quick-start sanity check”After docker compose up -d, verify the stack came up clean:
docker compose -f docker-compose.prod.yml psdocker compose -f docker-compose.prod.yml logs app --tail 50curl -fsS http://localhost:3000/healthThe health endpoint returns 200 OK once the database is reachable
and migrations are applied. If it’s still 503, watch the logs —
migrations or the airport seed (~9000 rows on first boot) may still
be running.
Move on to First Run for the admin-user handover.