Skip to content

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.

RequirementMinimum
Docker Engine24.x or newer
Docker Composev2 (built into recent Docker Desktop / Docker CLI)
Disk~1 GB for the app + database; ~5 GB more if you keep the optional Ollama container
RAM512 MB without Ollama, ~4 GB with Ollama running gemma3:12b
OSAny Linux host that runs Docker — bare metal, LXC, VM, NAS appliance

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.

Terminal window
# 1. Grab the production compose file
curl -O https://raw.githubusercontent.com/Abrechen2/TravStats/Main/docker-compose.prod.yml
# 2. Set a strong database password
echo "DB_PASSWORD=$(openssl rand -base64 32)" > .env
# 3. Start the stack
docker compose -f docker-compose.prod.yml up -d

Open http://localhost:3000 and continue with the first-run wizard.

The compose file defines three services:

travstats-db Postgres 15 + PostGIS — your flights, trips, achievements, settings
travstats-app The TravStats application (Express backend + React frontend baked in)
travstats-ollama Local LLM — primary email parser (recommended) and boarding-pass vision tier

All three live on a single bridge network (travstats-network) and each gets its own named volume:

VolumeHolds
travstats-db-dataThe Postgres data directory
travstats-app-dataAuto-generated secrets (JWT/encryption keys), uploads, log files
travstats-ollama-dataPulled LLM models — only allocated if you keep the container

Put these in .env next to DB_PASSWORD if your setup needs them. Most installations don’t.

VariableDefaultWhen to set
APP_PORT3000Conflicts with another service on port 3000
TZUTCLeave 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_URLhttp://ollama:11434Pointing at a Mac/PC running Ollama elsewhere on your LAN
VERSIONlatestPinning a specific image tag instead of tracking latest

TravStats is happy to live behind any reverse proxy that forwards Host and X-Forwarded-Proto. Confirmed working in the wild:

  • Cloudflare Tunnel — install cloudflared on the host (or in a sidecar), point a public hostname at http://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; and Host $host. TravStats auto-detects secure cookies from there.
  • Tailscale Funnel / Serve — works as long as X-Forwarded-Proto: https arrives.

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.

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:

Terminal window
docker compose -f docker-compose.prod.yml stop ollama
docker compose -f docker-compose.prod.yml rm -f ollama

Then 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.

Already running Postgres in your homelab? Drop the bundled db service from the compose file and set DATABASE_URL directly:

Terminal window
DATABASE_URL=postgresql://travstats:<password>@postgres.lan:5432/travstats

The 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.

After docker compose up -d, verify the stack came up clean:

Terminal window
docker compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.prod.yml logs app --tail 50
curl -fsS http://localhost:3000/health

The 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.