Repository navigation
Build the nginx image from a CI-published frontend image, not a cached from-source build #205
Description
Problem
nginx/Dockerfile builds the web frontend from source on every stack: its first RUN asks the GitHub API for the release named by frontend_release_path (latest by default), downloads that tarball, and a later step runs a full flutter build web.
Docker caches that RUN layer by its command text, not by what the API returns. With the default frontend_version: latest the text never changes, so once any stack on a host has built it, every later build, including a freshly generated stack, reuses the cached layer and serves whichever release that first build fetched. Nothing in the build output says so except a CACHED on that step.
Seen while testing soliplex/soliplex#1414: a new stack built on 2026-09-27 served the April 0.83.0 client (fadb0e3) rather than the current v0.106.1+91 (884cb36). The old client leaves the # in its return_to unencoded, so the stack showed login behaviour that no current client has. Only docker compose build --no-cache nginx picked up the real latest release.
The from-source build is also the slowest part of bringing a stack up (a Flutter toolchain image plus a full release build), and it repeats work that soliplex/frontend's CI already does: flutter.yaml runs flutter build web --release and uploads the result as a workflow artifact, which is then thrown away.
Upstream
Two stacked soliplex/frontend PRs provide the image this needs:
- feat(docker): cache-bust web assets w/ '$RELEASE_HASH' frontend#614 (closes Cache-bust Flutter web assets in the Docker build frontend#612): an optional
RELEASE_HASHbuild arg, which cache-busts the web build. - ci(docker): push web container images on release frontend#615 (closes Publish the built web app as a container image on each release frontend#613), stacked on #614: a
webtarget holding only/build/web, published on each release as
ghcr.io/soliplex/frontend-webforlinux/amd64andlinux/arm64.
What the published image provides:
- Cache busting: done upstream. Assets live under
/build/web/<tag>/,
andindex.htmlshould be servedno-cache. The template's
post-build-cache-bust.shcan go. - Relative URLs: asset URLs resolve against
<base href>(default/),
so the same image serves under a sub-path by editing only the<base href>
inindex.html. That is whatnginxlanding page #104's move to/frontend/needs. - Absolute
assetBase: set vianew URL("<tag>/", document.baseURI).
This fixes a bug the template's script has today: its schemeless
assetBase: "/<hash>/"makespackage_info_plusthrow, so the Versions
screen's App row shows "Unavailable". - Build flags:
--no-web-resources-cdn(CanvasKit bundled), with no
deprecated--pwa-strategy. - Tags: release
v0.109.0+95publishes0.109.0,0.109.0-95,
0.109(moving), andlatest(moving).
Plan here (once #614 and #615 land and a release is published)
-
nginx/Dockerfile: replace the Flutter builder stage with the
published image:FROM ghcr.io/soliplex/frontend-web:${frontend_image_tag} AS frontend FROM debian:trixie COPY --from=frontend /build/web /app/build/web
Keep what is per-stack:
nginx.conf, the error pages, the self-signed TLS
material, and thePUID/PGIDuser. Delete
nginx/post-build-cache-bust.sh. -
Generator: replace the derived
frontend_release_pathwith
frontend_image_tag, mapped fromfrontend_version:frontend_versionfrontend_image_taglatestlatestv0.109.0+950.109.0-95v0.109.00.109.0Tighten
FRONTEND_VERSION_REto match. Then updatePARAMETERS.md,
SKILL.md(frontend version choices),migrate_layout.py(the derived-key
list), the nginx rules inrefresh_skill_template.py, and their tests. -
Docs: picking up a new frontend release becomes
docker compose build --pull nginx, not--no-cache. -
Existing stacks: the next
docker compose build --pull nginxafter
regenerating or migrating moves them onto the image. Nothing in their data
changes.
Notes
- No image yet: releases before #615 have no published image. A
workflow_dispatchofimage.yamlwithtag: v0.109.0+95can backfill the
current release, so the template has something to pin on day one. - Public package: the
frontend-webGHCR package must be public, or stack
builds need registry credentials. - Fonts aren't bundled (no change from today):
--no-web-resources-cdn
bundles CanvasKit only. Text fonts still load fromfonts.gstatic.com, so a
stack with no internet access renders icons but no text.
Open question
- Should
frontend_version: lateststay the default? With--pullit now
follows the registry rather than a stale layer, but pinning the current
release at generation time would make a stack reproducible by default.
Activity