Serve dist from stock caddy:2-alpine + mounted bundle (drop custom runtime image) #22

Closed
opened 2026-06-06 01:55:56 +00:00 by coilysiren · 0 comments
Owner

Goal

Make galaxy-gen's runtime a literal, unmodified caddy:2-alpine with the build output mounted, instead of a custom runtime image with the assets baked in.

Context

Dockerfile stage 2 is already FROM caddy:2-alpine + COPY --from=builder /app/dist /usr/share/caddy. The custom image exists only because stage 1 is a real compile - Rust -> wasm-pack -> webpack, with the load-bearing binaryen-119 pin that dodges the WebAssembly.Table.grow() browser crash. That compile is intrinsic and stays in a build stage. Only the serving layer is being changed.

Plan

  1. Final build stage becomes a pure data image: FROM scratch + COPY --from=builder /app/dist /. CI pushes it to the in-cluster registry as 192.168.0.194:30500/galaxy-gen-dist:<sha>. That image is the bundle - no base image, no server, nothing to run.
  2. The Deployment runs stock caddy:2-alpine with the bundle delivered via volume.

Delivery path - two options

  • Image volume (volumes[].image) - purest, exactly the "stock caddy + mounted bundle" shape, bundle stays pure FROM scratch. Needs the ImageVolume feature: GA/on-by-default on k8s 1.33+, but alpha/off on the current server (v1.32.3+k3s1). Using it now means enabling an alpha feature gate on the single control-plane node + a k3s restart.
  • initContainer + emptyDir - bundle image is FROM busybox + COPY dist /dist, an initContainer cp -r /dist/* /www into a shared emptyDir, stock caddy:2-alpine serves /www. Works on 1.32 today, zero cluster change, one tiny extra container in the pod spec. Recommended now. Swap to the image-volume form for free once kai-server is on k3s 1.33+.

Rejected

  • Embedding the wasm into a single text file (base64 data URI, or inlining all into one index.html) is possible but a pessimization: ~33% size inflation, loses instantiateStreaming (decode-before-compile stall), loses separate wasm caching. Keep dist/ as an immutable bundle (a directory), not one file.
  • SENTRY_DSN stays baked at webpack build time (DefinePlugin) in the build stage - browser is the runtime, a k8s env var would be too late.

Shares the stock-caddy + mounted-bundle deploy pattern with the repos.coilysiren.me catalog-graph viewer. Whichever delivery path lands here, that one follows.

## Goal Make galaxy-gen's runtime a **literal, unmodified `caddy:2-alpine`** with the build output mounted, instead of a custom runtime image with the assets baked in. ## Context `Dockerfile` stage 2 is already `FROM caddy:2-alpine` + `COPY --from=builder /app/dist /usr/share/caddy`. The custom image exists only because **stage 1 is a real compile** - Rust -> `wasm-pack` -> webpack, with the load-bearing binaryen-119 pin that dodges the `WebAssembly.Table.grow()` browser crash. That compile is intrinsic and stays in a build stage. Only the serving layer is being changed. ## Plan 1. Final build stage becomes a **pure data image**: `FROM scratch` + `COPY --from=builder /app/dist /`. CI pushes it to the in-cluster registry as `192.168.0.194:30500/galaxy-gen-dist:<sha>`. That image *is* the bundle - no base image, no server, nothing to run. 2. The Deployment runs stock `caddy:2-alpine` with the bundle delivered via volume. ## Delivery path - two options - **Image volume (`volumes[].image`)** - purest, exactly the "stock caddy + mounted bundle" shape, bundle stays pure `FROM scratch`. Needs the `ImageVolume` feature: GA/on-by-default on k8s 1.33+, but **alpha/off** on the current server (`v1.32.3+k3s1`). Using it now means enabling an alpha feature gate on the single control-plane node + a k3s restart. - **initContainer + emptyDir** - bundle image is `FROM busybox` + `COPY dist /dist`, an initContainer `cp -r /dist/* /www` into a shared `emptyDir`, stock `caddy:2-alpine` serves `/www`. Works on 1.32 today, zero cluster change, one tiny extra container in the pod spec. **Recommended now.** Swap to the image-volume form for free once kai-server is on k3s 1.33+. ## Rejected - Embedding the wasm into a single text file (base64 data URI, or inlining all into one `index.html`) is possible but a pessimization: ~33% size inflation, loses `instantiateStreaming` (decode-before-compile stall), loses separate wasm caching. Keep `dist/` as an immutable bundle (a directory), not one file. - `SENTRY_DSN` stays baked at webpack build time (DefinePlugin) in the build stage - browser is the runtime, a k8s env var would be too late. ## Cross-link Shares the stock-caddy + mounted-bundle deploy pattern with the `repos.coilysiren.me` catalog-graph viewer. Whichever delivery path lands here, that one follows.
Sign in to join this conversation.
No description provided.