18 Deployment
Emily edited this page 2026-11-11 10:36:59 +01:00

Deployment

Hosted web build

The public web app is https://games.emilyh.de. It is a static Flutter web build served through a reverse proxy.

The web build:

  • Uses path-based URLs (usePathUrlStrategy).
  • Requires an SPA fallback: unknown paths must return index.html.
  • Reads version.json for the current build and latest.json for update notices.
  • Serves .well-known/assetlinks.json for Android App Links.
  • Omits the Apple App Site Association file unless APPLE_TEAM_ID is supplied at build time.

Static hosting

flutter build web --release

Upload the contents of build/web/ to any static host. Configure:

  • SPA fallback to index.html.
  • application/manifest+json for manifest.json.
  • No-cache or short-cache headers for index.html, version.json and latest.json.
  • Long cache headers for hashed Flutter assets.
  • HTTPS.
  • .well-known/ served as static files.

WEB_HOSTING.md in the repository has complete Nginx, Caddy, Docker, subpath and cache examples.

Docker

deploy/Dockerfile builds the app and serves it with Nginx on port 6969:

docker build -f deploy/Dockerfile -t universal-gaming-web:2.12.2 .
docker run --rm -p 6969:6969 universal-gaming-web:2.12.2

The build stage is pinned to:

  • linux/amd64 for Apple Silicon compatibility.
  • Flutter 3.47.9.
  • A SHA-256 checksum for the Flutter archive.

The image is published to the Gitea registry:

docker pull git.emilyh.de/emily/universal-gaming-web:2.12.2
docker pull git.emilyh.de/emily/universal-gaming-web:latest

deploy/docker-compose.yml runs the web container with WEB_IMAGE overridable. deploy/Caddyfile and deploy/nginx.conf show reverse-proxy examples. The Nginx config trusts loopback and private proxy ranges for X-Forwarded-For and logs xff=.

Backend

Save backups need backend 1.5.0 or newer: migration 3 adds the game_saves table and the feature is gated by SAVE_BACKUP_ENABLED.

The backend is deployed separately. It uses PostgreSQL and the update.sh script for safe upgrades. See:

Typical production topology:

Browser / app
     |
     v
Caddy or Nginx (HTTPS)
     |-- games.example.com   -> web container :6969
     `-- account.example.com -> backend container :6767 -> PostgreSQL

Release artifacts

A release includes:

  • Android APK and AAB.
  • Unsigned iOS IPA.
  • macOS DMG.
  • Web ZIP.
  • SHA256SUMS.txt.

Artifacts are built locally, hashed, and attached to a Gitea release. The web image is built and pushed separately.

Release process

  1. Bump the version and update release metadata (web/latest.json, Windows resources, docs, Compose tags).
  2. Run tests and flutter analyze.
  3. Build artifacts.
  4. Create SHA256SUMS.txt.
  5. Commit, tag vX.Y.Z, push the tag.
  6. Create the Gitea release and upload assets.
  7. Build and push the web image.
  8. Deploy the web container.
  9. Verify:
    • https://games.emilyh.de/version.json
    • https://games.emilyh.de/latest.json
    • backend /healthz
    • account sign-in and leaderboard read
  10. Optionally prune old images.

Upgrade the web build

On the deployment host:

git pull --ff-only
docker build -f deploy/Dockerfile \
  -t git.emilyh.de/emily/universal-gaming-web:2.12.2 .
docker push git.emilyh.de/emily/universal-gaming-web:2.12.2
docker push git.emilyh.de/emily/universal-gaming-web:latest

cd deploy
WEB_IMAGE=git.emilyh.de/emily/universal-gaming-web:2.12.2 \
  docker compose up -d

Then hard-refresh the browser or clear the Flutter service worker cache if an old version remains.

Rollback

The web build is stateless. To roll back, deploy the previous image tag:

WEB_IMAGE=git.emilyh.de/emily/universal-gaming-web:2.8.1 \
  docker compose up -d

Backend rollback is more involved because of database migrations; see the backend deployment page.