Skip to content

First Run

The first time TravStats starts, it generates the secrets it needs, applies all database migrations, and seeds the global airport database. This page covers what happens, what you do, and how to verify the stack is healthy before relying on it.

Watch the first boot with docker compose logs -f app. You’ll see roughly this sequence:

  1. Data directory layout is ensured under /app/data/ (owned by node:node, uid 1000).
  2. JWT secret is generated (openssl rand -hex 32) and persisted to /app/data/secrets/jwt.secret. On subsequent boots it’s loaded from there — your sessions survive container recreation.
  3. Database connectivity is checked (up to 30 retries, 2 s apart).
  4. Prisma migrations are applied (prisma migrate deploy). On a fresh database this creates all tables in one shot.
  5. Time-semantics backfill runs once on existing installs that pre-date v1.2.0 (a no-op on fresh installs).
  6. Airport database is seeded from a packaged OpenFlights CSV (~9000 rows). Skipped on subsequent boots once the table is non-empty.
  7. nginx binds to port 80, backend binds to 8000 internally — only port 80 is exposed via the compose file.

When you see [entrypoint] TravStats is ready (nginx on :80, backend on :8000), the stack is up.

There is no default admin user. TravStats hands you a setup wizard the first time you open it.

  1. Visit http://<your-host>:3000 (or your reverse-proxy URL).
  2. The frontend hits /api/v1/setup/status and gets requiresSetup: true. You’ll be redirected to Setup.
  3. Fill in:
    • Username + password — your admin account. Password must be at least 8 characters.
    • Instance name — what shows in the browser title and emails. “My TravStats”, “Wittke Family Logbook” — whatever fits.
    • Public URL — used in emails (invitations, password reset). Set to whatever URL users will actually hit.
    • User cap — how many users this instance can hold. Default is unlimited; useful for invite-only setups.
    • Registration mode — public sign-up (open) or invite-only (closed). Can be flipped any time later under Admin → Settings.
  4. Submit. The backend creates the first admin user and signs you in via an HttpOnly cookie. You land on the dashboard.

To keep first-run as short as possible, these are configured later under Admin → Settings (no env-var editing required):

  • AirLabs / Aviationstack / OpenSky API keys (encrypted at rest, used to enrich flights with aircraft type, distance, route history)
  • Ollama URL + model (default gemma3:12b, fallback when the template engine doesn’t recognise an email)
  • SMTP (for invitation emails, password reset, flight reminders)
  • Backup schedule + retention, optional WebDAV off-site sync (Nextcloud, HiDrive, …)
  • Logging level (defaults to info, can be raised to debug when troubleshooting)

You can use TravStats fully without any of these — they unlock specific features rather than gating core functionality.

Three quick checks the homelab muscle-memory will already know:

Terminal window
# Container health
docker compose -f docker-compose.prod.yml ps
# App reports healthy?
curl -fsS http://localhost:3000/health
# → 200 OK with JSON body once migrations are done
# Database reachable?
docker exec travstats-db pg_isready -U flights
# → /var/run/postgresql:5432 - accepting connections

If /health keeps returning 503, watch the logs — migrations or the airport seed may still be running on first boot. The seed does ~9000 inserts and takes 30–60 s on a Pi-class CPU.

If you finish the wizard with a username you already regret, there are two ways out:

  • From the UI (cleanest) — log in, Admin → Users, create a second admin, sign in as the new one, delete the first.
  • CLI fallbackdocker exec -it travstats-app node /app/backend/dist/scripts/createAdmin.js reads ADMIN_USERNAME / ADMIN_PASSWORD from the environment and creates an admin if none exists. (No-op if the database already has an admin user — won’t overwrite.)

Lost the admin password before SMTP is configured

Section titled “Lost the admin password before SMTP is configured”

The “forgot password” flow needs SMTP. If you haven’t set it up yet:

Terminal window
# Open a Postgres shell inside the db container
docker exec -it travstats-db psql -U flights -d flights
# Reset the admin password to a known bcrypt hash
# (this is the bcrypt of "tempchangeme" with cost 10)
UPDATE users
SET password_hash = '$2b$10$WdN5oW3bI3p5LKDGo6gBb.MR0J2I3Sw6NkN4QrJj0Dp/2nXFqtbQO',
must_change_password = true
WHERE is_admin = true;
\q

Sign in with tempchangeme, change the password immediately, then go to Admin → Settings and configure SMTP so this never happens again.

  • Add your first flight manually or by parsing a confirmation email.
  • Configure backups — the default schedule writes a daily dump to /app/data/backups, optional WebDAV sync ships them off-host.
  • Updating — what to expect when a new release lands.