Skip to content

Updating

TravStats follows semantic versioning and ships through GHCR with a Docker Hub mirror for stable releases. The update flow is two commands. Migrations run automatically.

TagWhat it points atWhen to use
:X.Y.ZAn exact released version (e.g. :1.3.0)Immutable pin — you control upgrades manually
:stableSame image as the latest X.Y.Z final release”Production trunk” — promoted only after RC verification
:latestSame image as :stableDefault if you don’t pin in compose
:rc-latestThe most recent Release Candidate (e.g. 1.3.0-rc.2)Beta-test the next release on a side instance
:X.Y.Z-rc.NAn exact RC buildPin a specific RC — no auto-rollover

:rc-latest lives on Docker Hub too (since v1.3.0) — Unraid, Synology and Portainer users can pull RCs without switching registries. Immutable RC tags (:1.3.0-rc.1, :2.0.0-beta.8) stay on GHCR only.

Terminal window
cd /opt/travstats # wherever your docker-compose.prod.yml lives
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml logs -f app

That’s it. The entrypoint takes care of:

  1. Loading the persisted JWT secret from /app/data/secrets/jwt.secret.
  2. Auto-resolving any failed migrations from a previous boot (prisma migrate resolve --rolled-back … against the marker rows).
  3. Running new migrations (prisma migrate deploy).
  4. Running idempotent backfill scripts (e.g. backfillTimeSemantics.js on the v1.2.0 → v1.3.0 boundary). Subsequent boots return zero rows and finish in milliseconds.

A successful upgrade ends with the [entrypoint] TravStats is ready line and /health returning 200.

If you want full control over upgrades — recommended for production:

# docker-compose.prod.yml (excerpt)
services:
app:
image: ghcr.io/abrechen2/travstats:1.3.0

Or via .env:

Terminal window
VERSION=1.3.0

The compose file already references ${VERSION:-latest}, so setting VERSION pins the tag without touching the YAML.

Read the CHANGELOG first — every entry calls out:

  • Breaking changes (rare; major versions only)
  • Database migrations that run automatically (and what they do)
  • New required env vars (almost never — most things move into the admin UI instead)

Take a database backup, especially before any minor version bump. TravStats writes daily backups to /app/data/backups automatically once you’ve enabled the schedule, but a fresh dump right before upgrade is cheap insurance:

Terminal window
docker exec travstats-db pg_dump -U flights flights > flights-pre-upgrade.sql

If the new version misbehaves, roll back to the previous image tag before the database has accumulated new schema you can’t undo:

.env
VERSION=1.2.1 # the previous version
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

Prisma migrations are forward-only by design — they don’t auto-revert when you downgrade the image. The new schema stays. If the failed upgrade introduced columns or tables the older code can’t tolerate, you’ll need to restore the pre-upgrade SQL dump:

Terminal window
# Stop the app first so it can't write to the rolling-back DB
docker compose -f docker-compose.prod.yml stop app
# Drop and recreate the database from the dump
docker exec -i travstats-db psql -U flights -d flights -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
docker exec -i travstats-db psql -U flights flights < flights-pre-upgrade.sql
# Bring the (older) app back up
docker compose -f docker-compose.prod.yml up -d app

In practice this is rare — TravStats migrations are designed to be idempotent and additive. Most “upgrade went wrong” cases recover by just downgrading the image tag.

TravStats checks GitHub Releases every six hours and shows a pulsing yellow Update badge in the header when a newer final release is available (RCs and pre-releases are filtered out). Click for the version, release date, a 600-character preview of the release notes, and a link to the full notes. “Ignore this version” hides the badge until something even newer ships.

If your instance is air-gapped or firewalled, the GitHub call fails silently — you just won’t see the badge. No errors are thrown.

Old image tags accumulate in the local Docker storage. Periodic prune:

Terminal window
docker image prune -f
docker system prune -f --filter "until=168h" # 7 days

Both are safe — they touch unused images only, never the running stack.