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.
Tags TravStats publishes
Section titled “Tags TravStats publishes”| Tag | What it points at | When to use |
|---|---|---|
:X.Y.Z | An exact released version (e.g. :1.3.0) | Immutable pin — you control upgrades manually |
:stable | Same image as the latest X.Y.Z final release | ”Production trunk” — promoted only after RC verification |
:latest | Same image as :stable | Default if you don’t pin in compose |
:rc-latest | The most recent Release Candidate (e.g. 1.3.0-rc.2) | Beta-test the next release on a side instance |
:X.Y.Z-rc.N | An exact RC build | Pin 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.
The standard update flow
Section titled “The standard update flow”cd /opt/travstats # wherever your docker-compose.prod.yml livesdocker compose -f docker-compose.prod.yml pulldocker compose -f docker-compose.prod.yml up -ddocker compose -f docker-compose.prod.yml logs -f appThat’s it. The entrypoint takes care of:
- Loading the persisted JWT secret from
/app/data/secrets/jwt.secret. - Auto-resolving any failed migrations from a previous boot
(
prisma migrate resolve --rolled-back …against the marker rows). - Running new migrations (
prisma migrate deploy). - Running idempotent backfill scripts (e.g.
backfillTimeSemantics.json 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.
Pinning a version
Section titled “Pinning a version”If you want full control over upgrades — recommended for production:
# docker-compose.prod.yml (excerpt)services: app: image: ghcr.io/abrechen2/travstats:1.3.0Or via .env:
VERSION=1.3.0The compose file already references ${VERSION:-latest}, so setting
VERSION pins the tag without touching the YAML.
Before you upgrade
Section titled “Before you upgrade”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:
docker exec travstats-db pg_dump -U flights flights > flights-pre-upgrade.sqlRolling back
Section titled “Rolling back”If the new version misbehaves, roll back to the previous image tag before the database has accumulated new schema you can’t undo:
VERSION=1.2.1 # the previous version
docker compose -f docker-compose.prod.yml pulldocker compose -f docker-compose.prod.yml up -dPrisma 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:
# Stop the app first so it can't write to the rolling-back DBdocker compose -f docker-compose.prod.yml stop app
# Drop and recreate the database from the dumpdocker 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 updocker compose -f docker-compose.prod.yml up -d appIn 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.
Update banner inside the app
Section titled “Update banner inside the app”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.
Container image cleanup
Section titled “Container image cleanup”Old image tags accumulate in the local Docker storage. Periodic prune:
docker image prune -fdocker system prune -f --filter "until=168h" # 7 daysBoth are safe — they touch unused images only, never the running stack.