Skip to content

Singleton mode: vite build runs on every container start β€” 2–5 min boot, blocks rolling updatesΒ #269

Description

@andersliland

Summary

The official ghcr.io/eyeix/meilisearch-ui Docker image runs pnpm run build at container start (see scripts/cmd.sh), so every pod restart spends 2–5 min on a production Vite build before serving traffic. In a Kubernetes rolling update with the standard maxUnavailable: 0 strategy this blocks the rollout for the full build duration on every replica.

Root cause: the singleton-mode runtime config is read from VITE_SINGLETON_* env vars, which Vite resolves at build time, not runtime. The image therefore can't ship a pre-built bundle today without also shipping a way to inject the runtime values into the static assets after the build.

Repro

docker run --rm \
  -e SINGLETON_MODE=true \
  -e SINGLETON_HOST=https://demo.meilisearch.example \
  -e SINGLETON_API_KEY=test \
  -p 24900:24900 \
  ghcr.io/eyeix/meilisearch-ui:0.15.1

Observe vite vX.x building for production... taking 2–5 min on a 500m-CPU Kubernetes pod (or 30–60s on a developer laptop). vite preview only starts after the build finishes.

Impact

  • Cold start latency: pod is not Ready for 2–5 min; readiness probes must be tuned to a 5–10 min startupProbe.failureThreshold to survive. We use failureThreshold: 60 with periodSeconds: 10.
  • Rolling-update lockstep: maxUnavailable: 0 blocks the old pod from terminating until the new one passes its build, doubling pod resource use for that window.
  • Key rotation: any secret rotation (Stakater Reloader, manual kubectl rollout restart) triggers a fresh 2–5 min build.

Suggested fix

Two equivalent approaches, both common in Vite-served apps:

  1. Placeholder string-replacement at container start. Bake the bundle once at image-build time with sentinel placeholders (e.g. __MEILI_SINGLETON_HOST__, __MEILI_SINGLETON_API_KEY__), then sed -i over dist/assets/*.{js,css} in the entrypoint to replace them with the runtime env values before vite preview. Boot time drops to O(seconds). PR with this approach attached.
  2. Runtime config endpoint. Ship dist/config.js that exports window.__MEILI_UI_CONFIG__, populate it from env in the entrypoint, and have the app read from window instead of import.meta.env. Avoids touching the bundled JS at all.

(1) is the minimal-diff option and what most Docker-deployed Vite apps do β€” opened a draft PR with that approach: #270.

Workaround

Until upstream lands a fix we patch the entrypoint downstream. Removing the workaround will be a one-line image-tag bump.

Environment

  • Image: ghcr.io/eyeix/meilisearch-ui:0.15.1
  • Vite: 6.x (per build log)
  • Runtime: GKE Autopilot, 500m CPU / 2 Gi mem container
  • Workload: Deployment with replicas: 1, maxUnavailable: 0

Happy to iterate on the PR if a different approach is preferred.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions