0056. Admin and legacy front served by nginx-unprivileged with runtime JSON config
Generated from docs/decisions/0056-nginx-unprivileged-images.md
- Status: Accepted
- Date: 2026-10-09
- Plan:
docs/plans/leftovers-0-2.md(L0-16)
Context and problem statement
Section titled “Context and problem statement”The admin and legacy front images serve static builds from PHP + Apache, only to inject runtime settings. The product owner approved switching to nginx-unprivileged on 2026-10-09.
Considered options
Section titled “Considered options”nginx-unprivilegedwith an entrypoint that writesruntime-config.jsonfrom an allow-list of env variables.- Keep PHP + Apache.
- Bake settings at build time.
Decision
Section titled “Decision”Option 1:
- Server. Port 8080, non-root, SPA fallback, long cache for hashed assets.
- Runtime config. The apps fetch
runtime-config.jsonbefore boot. - Headers. Security headers and the CSP come from the reverse proxy (ADR 0044).
Implementation notes
Section titled “Implementation notes”- Settings keep their names: the entrypoint writes every
REACT_APP_*(admin) orVITE_APP_*(front) variable, so existing deployments only change the port (80 to 8080). The plan’sULAMS_*allow-list was not introduced, to avoid renaming settings that the apps and the docs already use. - The page loads
runtime-config.jsonwith a synchronous request in an inline script, before any bundle, because Sentry and the tenant resolution read the values when their modules load. - Source maps are removed from the images;
MULTI_DOMAINSand the PHP front controller are gone.
Consequences
Section titled “Consequences”- Good: smaller images, no PHP in static images, non-root.
- Bad: the apps’ boot code changes, so runtime config needs a fetch before render.