Skip to content

Build the nginx image from a CI-published frontend image, not a cached from-source build #205

Closed

Description

@tseaver

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:

What the published image provides:

  • Cache busting: done upstream. Assets live under /build/web/<tag>/,
    and index.html should be served no-cache. The template's
    post-build-cache-bust.sh can 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>
    in index.html. That is what nginx landing page #104's move to /frontend/ needs.
  • Absolute assetBase: set via new URL("<tag>/", document.baseURI).
    This fixes a bug the template's script has today: its schemeless
    assetBase: "/<hash>/" makes package_info_plus throw, 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+95 publishes 0.109.0, 0.109.0-95,
    0.109 (moving), and latest (moving).

Plan here (once #614 and #615 land and a release is published)

  1. 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 the PUID/PGID user. Delete
    nginx/post-build-cache-bust.sh.

  2. Generator: replace the derived frontend_release_path with
    frontend_image_tag, mapped from frontend_version:

    frontend_version frontend_image_tag
    latest latest
    v0.109.0+95 0.109.0-95
    v0.109.0 0.109.0

    Tighten FRONTEND_VERSION_RE to match. Then update PARAMETERS.md,
    SKILL.md (frontend version choices), migrate_layout.py (the derived-key
    list), the nginx rules in refresh_skill_template.py, and their tests.

  3. Docs: picking up a new frontend release becomes
    docker compose build --pull nginx, not --no-cache.

  4. Existing stacks: the next docker compose build --pull nginx after
    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_dispatch of image.yaml with tag: v0.109.0+95 can backfill the
    current release, so the template has something to pin on day one.
  • Public package: the frontend-web GHCR 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 from fonts.gstatic.com, so a
    stack with no internet access renders icons but no text.

Open question

  • Should frontend_version: latest stay the default? With --pull it now
    follows the registry rather than a stale layer, but pinning the current
    release at generation time would make a stack reproducible by default.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions