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.jsonfor the current build andlatest.jsonfor update notices. - Serves
.well-known/assetlinks.jsonfor Android App Links. - Omits the Apple App Site Association file unless
APPLE_TEAM_IDis 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+jsonformanifest.json.- No-cache or short-cache headers for
index.html,version.jsonandlatest.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/amd64for 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
- Bump the version and update release metadata (
web/latest.json, Windows resources, docs, Compose tags). - Run tests and
flutter analyze. - Build artifacts.
- Create
SHA256SUMS.txt. - Commit, tag
vX.Y.Z, push the tag. - Create the Gitea release and upload assets.
- Build and push the web image.
- Deploy the web container.
- Verify:
https://games.emilyh.de/version.jsonhttps://games.emilyh.de/latest.json- backend
/healthz - account sign-in and leaderboard read
- 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.
Universal Gaming
Repositories
Universal Gaming is a private project by Emily. The app is offline-first; accounts, online chess and the competitive leaderboard are optional.