Skip to content

feat(docker): substitute SINGLETON_* at container start instead of build-at-boot#270

Draft
andersliland wants to merge 1 commit into
eyeix:mainfrom
andersliland:feat/runtime-singleton-config
Draft

feat(docker): substitute SINGLETON_* at container start instead of build-at-boot#270
andersliland wants to merge 1 commit into
eyeix:mainfrom
andersliland:feat/runtime-singleton-config

Conversation

@andersliland
Copy link
Copy Markdown

Fixes #269.

Summary

  • Bake the Vite bundle once at image build time using sentinel placeholders (__MEILI_UI_REPLACE_SINGLETON_{MODE,HOST,API_KEY}__).
  • At container start, scripts/cmd.sh sed-substitutes the real SINGLETON_* env values into the prebuilt dist/ and runs pnpm run preview.
  • Boot time drops from 2–5 min (full vite build on every container start) to seconds.

This makes secret rotation and rolling updates viable in production clusters: today, every kubectl rollout restart (or anything that triggers a pod restart, like Stakater Reloader on a secret change) blocks readiness for the full build duration.

Backwards compatibility

scripts/cmd.sh falls back to the previous build-at-start behavior when dist/ is empty, so:

  • Non-Docker deployments (e.g. Vercel) — unaffected, they build at deploy time with concrete env values via scripts/post-build.js.
  • Anyone running scripts/cmd.sh directly against a tree without a prebuilt dist/ — unaffected.
  • Existing Docker callers — same env-var contract (SINGLETON_MODE, SINGLETON_HOST, SINGLETON_API_KEY, BASE_PATH).

The placeholder strings are unique sentinels (__MEILI_UI_REPLACE_SINGLETON_*__) and follow the same pattern this repo already uses for MEILI_UI_REPLACE_BASE_PATH (see scripts/post-build.js).

Safety net

If a placeholder slips through the sed pass (e.g. the bundler emits an escaped form), the entrypoint exits non-zero rather than serve sentinel strings to the browser.

Test plan

  • docker build produces a dist/ with placeholders.
  • docker run with SINGLETON_MODE=true substitutes correctly; the bundle no longer contains placeholder strings.
  • docker run with no SINGLETON_* set substitutes placeholders to empty (multi-instance mode).
  • Backwards-compat: removing dist/ from the image falls back to build-at-start path.

Happy to iterate if a different approach (e.g. a dist/config.js runtime endpoint) is preferred — see linked issue for the alternative discussed.

…ild at boot

Bake the Vite bundle ONCE at image build time with sentinel placeholders
(`__MEILI_UI_REPLACE_SINGLETON_{MODE,HOST,API_KEY}__`) and substitute
the real values from the runtime environment via `sed` in the
entrypoint. Boot time drops from 2-5 min (full `vite build` on every
container start) to seconds. Secret rotation / rolling updates become
viable in production clusters.

Backwards-compatible: if `dist/` is empty the entrypoint falls back to
the previous build-at-start path, so non-Docker callers running
`scripts/cmd.sh` directly are unaffected.

Fixes eyeix#269
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

@andersliland is attempting to deploy a commit to the Eyeix Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Singleton mode: vite build runs on every container start — 2–5 min boot, blocks rolling updates

1 participant