Self-hosted travel logbook for small households and groups (1–10 users). It's a logbook, not a live tracker — you record trips manually, scan a boarding pass, or import a confirmation email, and TravStats turns them into history, stats and maps.
Log every flight you take (cruises landing in v2), visualise your routes on interactive 2D and 3D maps, collect 101 achievements, and import flights from boarding passes (QR / PDF417 / OCR), confirmation emails, or Excel/CSV — all on your own server, no cloud, no telemetry.
It's a logbook, not a live tracker — you record trips manually, scan a boarding pass, or import a confirmation email, and TravStats turns them into history, stats and maps. Flight data is optionally enriched from AirLabs / Aviationstack / OpenSky, but everything you record lives in your own PostgreSQL. No accounts on someone else's servers, no analytics, no ads.
- 🗺️ Six map modes — Routes, Heatmap, Hexagon, 3D columns, animated Trips, 3D Globe
- 📊 Year-over-year statistics across flights, distance, seats, classes, routes
- 🏆 101 Battlefield-style achievements across five categories
- 🎫 Boarding-pass scanner — QR / barcode / OCR
- 📧 Email import — plain text, HTML, Outlook
.msg,.eml, with optional local LLM parsing via Ollama - 📑 Excel/CSV round-trip import — export, edit in Excel, re-import; rows with an
idupdate existing flights - 🤖 Public REST API + OpenAPI 3.0 / Swagger UI — Personal Access Tokens with
read/write/adminscopes for AI agents and automation - 💾 Automated backups with retention + optional WebDAV sync
- 🔐 Invite-only by default — toggle public registration anytime from the admin UI; JWT in HttpOnly cookies, 18 rate limiters on sensitive endpoints
- 🌐 German + English UI with browser-locale auto-detection, i18n-ready
| Downloadable PNG certificate with your totals | 101 Battlefield-style achievements |
The only thing you set in a file is a database password. Everything else — instance name, user cap, API keys, Ollama model, backup schedule, WebDAV — is captured by the first-run setup wizard in the browser.
# 1. Grab the compose file
curl -O https://raw.githubusercontent.com/Abrechen2/TravStats/Main/docker-compose.prod.yml
# 2. Set one variable
echo "DB_PASSWORD=$(openssl rand -base64 32)" > .env
# 3. Start
docker compose -f docker-compose.prod.yml up -d
# 4. Open the setup wizard
open http://localhost:3000/setupThat's the complete install. The container seeds the airport database on first boot (~30 s), auto-generates a JWT secret, and the setup wizard persists instance settings to the database.
If you already run a Postgres server (homelab, managed DB), skip the
bundled db service by providing DATABASE_URL and removing the db and
depends_on blocks from the compose file:
DATABASE_URL=postgresql://user:pass@postgres.lan:5432/travstatsCommunity Apps templates live in a dedicated repo —
Abrechen2/docker-templates.
Install travstats-db from there first, then TravStats, set the
Database URL password, open /setup. Walk-through with screenshots in
docs/unraid/README.md.
Optional local LLM parsing: install the Ollama Community App (or run Ollama anywhere else on your network), pull
gemma3:12b(~7.5 GB), then point TravStats at it from Admin → Parser. Multi-flight confirmation mails and unknown airline templates are then handled locally — nothing leaves your network.
Both registries (GHCR and Docker Hub) carry the same digests for these moving tags. Pick the one your platform defaults to.
| Tag | Points to | Use for |
|---|---|---|
:latest, :stable |
Latest stable release (currently 1.2.1) |
Normal production. Auto-updates to the next promoted release. |
:X.Y.Z (e.g. :1.2.1) |
Pinned immutable release | Reproducible installs, audit, regulated environments. |
:rc-latest |
Newest Release Candidate (currently 1.3.0-rc.7) |
Beta testers — receive every fresh RC via docker compose pull. May include breaking schema changes across major bumps; an in-place backup is taken automatically on first start of a new major. |
Specific RC tags (:1.3.0-rc.1, :2.0.0-beta.8) and dev builds live on
GHCR only — Docker Hub only mirrors the moving tags above plus pinned
final releases.
Almost nothing to configure via environment variables. The setup wizard captures everything instance-level (name, public URL, user cap, registration mode) and the admin UI handles API keys, Ollama, backup schedule and WebDAV sync.
| Env variable | When to set it | Default |
|---|---|---|
DB_PASSWORD |
Always — shared secret between app and bundled Postgres | required |
APP_PORT |
Different host port | 3000 |
DATABASE_URL |
External Postgres instead of the bundled service | (derived from DB_PASSWORD) |
COOKIE_SECURE |
Reverse proxy doesn't send X-Forwarded-Proto |
(auto-detected) |
CORS_ORIGIN |
Frontend lives on a different hostname than the API | (same-origin only) |
TZ |
Non-UTC container clock (not recommended) | UTC |
See .env.prod.example for the annotated list.
- Instance name, public URL, user cap, registration mode
- AirLabs / OpenSky / Aviationstack API keys (encrypted at rest)
- Ollama endpoint + model (default
gemma3:12b) - Backup schedule and retention
- WebDAV off-site backup sync (Nextcloud, HiDrive, …)
- SMTP for invitation and password-reset emails
- Logging level and retention
TravStats works without any API key; manual flight entry and boarding-pass scanning cover the full feature set.
See CHANGELOG.md for the full history and ROADMAP.md for where things are heading (cruises module, CO₂ tracking, trip planner, PWA).
See SECURITY.md for the hardening summary, audit history, and verification commands. TL;DR: JWT in HttpOnly cookies, 18 distinct rate limiters, Zod validation on every endpoint, Prisma-parameterised queries, Helmet CSP, invite-only by default.
Report vulnerabilities via GitHub Security Advisories — please do not open a public issue.
TravStats ships an authenticated REST API for AI agents, automation scripts and integrations. Every flight, trip, airport and stat the web UI shows is reachable programmatically.
1. Mint a Personal Access Token: in the app, go to Settings →
API Tokens, give it a label and a scope (read, write, admin),
and copy the ts_pat_… value. The plaintext is shown exactly once —
only the bcrypt hash is persisted.
2. Browse the spec: open https://<your-host>/api/v1/docs
(Swagger UI) or fetch /api/v1/openapi.json for the raw OpenAPI 3.0
document. The spec is auto-generated from the same Zod schemas the
backend uses for validation, so it never drifts.
3. Call it: every endpoint accepts the token via the standard
Authorization header.
TOKEN="ts_pat_…"
# List your flights
curl -H "Authorization: Bearer $TOKEN" \
https://travstats.example.com/api/v1/flights
# Create a flight (write or admin scope required)
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"departure":{"iata":"FRA","lat":50.0379,"lon":8.5622},
"arrival":{"iata":"JFK","lat":40.6413,"lon":-73.7781},
"departureTime":"2026-05-01T08:00:00.000Z",
"arrivalTime":"2026-05-01T17:00:00.000Z",
"flightNumber":"LH400"}' \
https://travstats.example.com/api/v1/flightsPass ?merge=true on POST /flights to enrich an existing matching
flight (boarding-pass re-import, email confirmation upgrade) instead
of creating a duplicate. Read-only tokens are blocked from mutating
endpoints with 403; admin endpoints additionally require an
admin-scoped token even if the owning user is an admin.
Token requests get their own per-token rate-limit bucket so an aggressive script can't lock the user out of the web UI.
Bug reports, feature ideas and pull requests are welcome. The Report Bug button in the app copies an anonymised diagnostic bundle for you. See CONTRIBUTING.md for the full guide.
npm run install:all # install backend + frontend
npm run dev # backend :8000 + frontend :3000
npm run typecheck # tsc --noEmit on both
npm run test:frontend # Vitest (backend tests need Postgres)Deep-dive developer reference: CLAUDE.md.
Copyright © 2026 Dennis Wittke · AGPL-3.0-or-later
You may use, modify and redistribute TravStats, but if you run it as a web service (even modified) you must make the complete source code of your modifications available under the same licence. See LICENSE.
TravStats is a solo side project. If it's useful to you, a small donation via PayPal keeps the lights on for AirLabs quota top-ups. ❤️
⭐ Star on GitHub · Releases · Roadmap · Issues
Made with ❤️ and a bit of AI for flight enthusiasts.