Files
dotcom-arcade/docs/09-deployment/upgrade-guide.md
T
Darkraise 1973adabca docs: add upgrade guide for the single-image deployment
Deployments on 1.0.31 or earlier cannot take an image update without
changing their compose file and .env, and nothing in the repo said so.

Document the migration from the two-container setup to the single
flashpoint-web image, the environment variables added, removed, and
changed since 1.0.31, and per-version release notes for 1.0.32-1.0.36.
Link it from the README and both documentation indexes.
2026-08-12 10:02:15 +07:00

245 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Upgrade Guide and Release Notes
How to move an existing deployment to a newer version, and what changed in each
release. Versions follow the git tags published on Docker Hub.
> **Upgrading from 1.0.31 or earlier?** The deployment model changed in 1.0.32:
> the separate backend and frontend containers were replaced by a single image.
> Read [Upgrading from 1.0.31](#upgrading-from-1031) before pulling.
---
## Upgrading from 1.0.31
### What changed
Up to 1.0.31 a deployment ran two containers: `darkraise/flashpoint-backend`
(Node/Express, port 3100) and `darkraise/flashpoint-frontend` (nginx serving the
built React app on port 8080, proxying `/api` to the backend).
From 1.0.32 there is one image, `darkraise/flashpoint-web`. The Express backend
serves the built React UI and its SPA fallback alongside the API and game
content, so the whole app lives on one origin and one port.
```
Before (<= 1.0.31) After (>= 1.0.32)
flashpoint-frontend :80 -> nginx flashpoint-web :80 -> :3100
flashpoint-backend :3100 -> Express / built React UI
/api, /game-proxy, /game-zip
```
The old `flashpoint-backend` and `flashpoint-frontend` images are no longer
built or published. 1.0.31 is the last version available under those names.
### Migration steps
1. **Stop and remove the old stack.** From the directory holding your existing
compose file:
```bash
docker compose down
```
This removes the `flashpoint-backend` and `flashpoint-frontend` containers.
Named volumes and bind mounts are kept.
2. **Replace `docker-compose.yml`** with the single-service version from the
[README](../../README.md#2-create-docker-composeyml). The service is now named
`flashpoint-web`, maps `${WEB_PORT:-80}` to container port `3100`, and has no
`frontend` service and no `depends_on`.
3. **Update `.env`** — see the variable tables below. At minimum, delete
`API_PORT`, `BACKEND_HOST`, and `BACKEND_PORT`; they are no longer read by
anything.
4. **Check the Flashpoint mount.** As of 1.0.36 the shipped compose mounts it
read-write (no `:ro`). Downloading games, recording downloaded state in
`flashpoint.sqlite`, and metadata sync all write into that directory. Leaving
`:ro` in place still allows browsing and playing already-downloaded games, but
the Downloaded page will stay empty and the reconciler logs a warning on every
boot.
5. **Update your reverse proxy, if you have one.** Configurations that defined
two upstreams — the UI on the web port and the API on port 3100 — must be
collapsed into a single upstream on `WEB_PORT`. Any rule routing `/api` to
port 3100 will break, because nothing listens there on the host anymore.
Firewall rules opening port 3100 can be removed.
6. **Pull and start.**
```bash
docker compose pull && docker compose up -d
```
### What is preserved
Application data is untouched by the migration. `user.db` (accounts, playlists,
favourites, play history, settings) lives in the `DATA_PATH` bind mount at
`/app/data` and is used unchanged by the new image. Log and Flashpoint mounts
keep their paths. No database migration is required beyond the ones the backend
applies on startup.
### Environment variable changes
**Removed** — delete these from `.env`; they have no effect:
| Variable | Replacement |
| -------------- | ---------------------------------------------------- |
| `API_PORT` | `WEB_PORT` — one port now serves both the UI and API |
| `BACKEND_HOST` | none — there is no cross-container proxy |
| `BACKEND_PORT` | none — same reason |
`PORT` and `HOST` were documented as configurable before 1.0.32 but never were:
the backend binds `0.0.0.0:3100` unconditionally, and the Docker `EXPOSE` and
healthcheck depend on that. The docs were corrected rather than the code.
**Added:**
| Variable | Default | Since | Description |
| ------------------------------ | -------------------------------- | ------ | --------------------------------------------------------------- |
| `SERVE_FRONTEND` | `true` in production, else false | 1.0.32 | Serve the built UI and SPA fallback from the backend |
| `FRONTEND_DIST_PATH` | `/app/frontend/dist` in image | 1.0.32 | Where the built UI lives; rarely needs overriding |
| `FLASHPOINT_GAMES_PATH` | `<FLASHPOINT_PATH>/Data/Games` | 1.0.32 | Override the game ZIP directory |
| `OTEL_METRICS_EXPORT_INTERVAL` | `60000` | 1.0.32 | Metrics export interval in milliseconds |
| `OTEL_LOG_LEVEL` | `info` | 1.0.32 | OpenTelemetry SDK log level |
| `COOKIE_SECURE` | `auto` | 1.0.34 | Secure flag on auth cookies: `auto`, `true`, `false` |
| `UV_THREADPOOL_SIZE` | `16` | 1.0.36 | File I/O threads; Node's own default of 4 throttles image reads |
| `DB_WORKER_COUNT` | `4` | 1.0.36 | Read-only SQLite query worker threads (1–16) |
`UV_THREADPOOL_SIZE=16` is baked into the image, so it applies whether or not
your compose file passes it.
**Changed:**
| Variable | Change |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DOMAIN` | Now accepts a comma-separated list of origins. Same-origin requests are always allowed, so a standard single-image install needs no value at all. Default ports are normalized: `http://host:80` and `http://host` are treated as the same origin. |
| `IMAGE_TAG` | Selects the tag of `darkraise/flashpoint-web` instead of the two old images. |
| `OTEL_SERVICE_NAME` | Default changed from `flashpoint-web-backend` to `flashpoint-web`. Update dashboards and alert queries that filter on the old name. |
| `LOGS_PATH` | Dev compose mounts it at `/app/logs` directly; it previously mounted `${LOGS_PATH}/backend`. |
**Newly passed through compose** (these already existed in the backend, but the
shipped compose files now forward them with documented defaults):
`JWT_EXPIRES_IN`, `BCRYPT_SALT_ROUNDS`, `RATE_LIMIT_WINDOW_MS`,
`RATE_LIMIT_MAX_REQUESTS`, `HOME_RECENT_HOURS`, `ENABLE_CGI`.
`FLASHPOINT_PATH=/data/flashpoint` is now set explicitly in the dev compose file.
The backend's fallback only points there when `NODE_ENV=production`, so a
development container previously looked at the wrong path.
### Rolling back
Pin the old images (`darkraise/flashpoint-backend:1.0.31` and
`darkraise/flashpoint-frontend:1.0.31`) and restore the two-service compose file
and the `.env` entries removed in step 3. Application data written by 1.0.32+ is
readable by 1.0.31. Downloaded state is recorded in Flashpoint's own
`presentOnDisk` and `path` columns, which the Launcher already understands;
1.0.31 simply does not surface it.
---
## [1.0.36] — 2026-08-12
### Fixed
- **Flashpoint directory is mounted read-write by default.** The shipped compose
files mounted it `:ro`, which silently failed every write the app makes:
downloaded-state flags in `flashpoint.sqlite`, downloaded game data under
`Data/Games`, and metadata sync. If you copied an earlier compose file, drop
the `:ro` suffix.
- Image preloads are cancelled when a request is abandoned, so scrolling a large
library no longer queues work for images that left the viewport.
### Added
- A metadata sync can optionally download game images afterwards.
### Performance
- Heavy read-only queries run in SQLite worker threads (`DB_WORKER_COUNT`,
default 4). `better-sqlite3` is synchronous, and a filter-options query over a
slow mount was measured blocking the entire server for 167 seconds — `/health`
timed out along with everything else.
- `UV_THREADPOOL_SIZE` is set to 16 in the image. libuv's default of 4 throttled
every image and ZIP read in the process, which dominates on network storage.
## [1.0.35] — 2026-08-12
### Added
- Admins can choose the metadata source used for syncs.
### Fixed
- Ruffle installs in the background and into the directory that is actually
served (`frontend/dist/ruffle`). Startup previously awaited the download, so
the port stayed closed for minutes on slow storage — indefinitely if the
download stalled — and a fresh container re-downloaded the Ruffle it already
shipped.
- Locally served images are cached and no longer block on `stat`.
- `npm test` runs the backend suite once instead of entering watch mode.
## [1.0.34] — 2026-08-11
### Fixed
- **Auth cookies are marked `Secure` per connection, not per `NODE_ENV`.**
Production builds previously always set `Secure`, and browsers discard such a
cookie when it arrives over plain HTTP — logging LAN users straight back out
after login. The new default, `COOKIE_SECURE=auto`, decides per request based
on TLS (reading `X-Forwarded-Proto` when behind a proxy), so one deployment can
serve HTTP on a LAN and HTTPS through a proxy at once. Set `COOKIE_SECURE=true`
if your TLS-terminating proxy does not send that header.
### Documentation
- README carries a minimal working `.env` example and refreshed sections.
## [1.0.33] — 2026-08-11
### Fixed
- **CORS origins are normalized and same-origin requests are always allowed.**
With the UI and API on one origin, most deployments no longer need `DOMAIN` at
all. `DOMAIN` also accepts a comma-separated list, and explicit default ports
(`:80`, `:443`) match the port-less origin a browser actually sends.
- The dev compose file passes `FLASHPOINT_PATH=/data/flashpoint` explicitly.
### Performance
- Downloaded filter options are cached indefinitely.
## [1.0.32] — 2026-08-11
### Breaking
- **Single image deployment.** The backend and frontend images were collapsed
into `darkraise/flashpoint-web`, which serves the API, game content, and the
built UI from one Express process on port 3100. The `frontend` service, its
nginx config and entrypoint, and the per-service Dockerfiles are gone; the
release workflow builds one image instead of a two-service matrix. See
[Upgrading from 1.0.31](#upgrading-from-1031).
- `API_PORT`, `BACKEND_HOST`, and `BACKEND_PORT` were removed. Map a host port
with `WEB_PORT` instead.
### Added
- Downloaded games page, filter, and settings: browse games whose data is already
on disk, with the filter encoded in the URL and downloaded state reconciled
from disk at startup.
- 5-star game rating system.
- Setting to toggle the "I'm Feeling Lucky" button.
### Security
- The SPA-oriented CSP was ported from nginx into Helmet and hardened, dropping
`unsafe-inline` and `unsafe-eval`. The strict API-only policy still applies
when `SERVE_FRONTEND` is off.
### Fixed
- Guest browsing survives auth-only 401s instead of falling into a redirect loop
(thanks @MaxfieldKassel).
- Invalid `SQLITE_CACHE_SIZE` and `SQLITE_MMAP_SIZE` values fall back to their
defaults instead of becoming `NaN`.