chore(frontend): add multi-stage Dockerfile + nginx SPA config
Production image for the frontend dist. Stage 1 (build): node:22-alpine, `npm ci --ignore-scripts` from the committed lockfile, `npm run build`. Output lands in /app/dist. Stage 2 (runtime): docker.io/nginxinc/nginx-unprivileged:alpine. - Upstream-maintained variant that runs as the nginx user (uid 101) out of the box. /var/cache/nginx and /var/run/nginx are pre-owned, no chown gymnastics needed in our layer. Vanilla nginx:alpine fails at startup as non-root because client_temp mkdir is denied. - Listens on 8080 (non-privileged port, matches the unprivileged variant convention). - nginx.conf serves /usr/share/nginx/html with SPA `try_files` fallback for client-side routing, long-cache headers on /assets/ (Vite hashed bundles), a plaintext /healthz endpoint for Caddy / Prometheus blackbox, and server_tokens off. .dockerignore excludes node_modules, dist, .vite, coverage, playwright-report, .env*, .git, editor dirs. Keeps .env.example. Smoke local validated with `podman build -t mimic-frontend:smoke .` and `podman run -p 127.0.0.1:18080:8080`: /healthz -> 200 "ok" / -> 200 index.html (508 B) /spa/x -> 200 (SPA fallback) /assets -> Cache-Control: max-age=31536000, public, immutable
This commit is contained in:
22
frontend/.dockerignore
Normal file
22
frontend/.dockerignore
Normal file
@@ -0,0 +1,22 @@
|
||||
node_modules
|
||||
dist
|
||||
.vite
|
||||
coverage
|
||||
playwright-report
|
||||
test-results
|
||||
.eslintcache
|
||||
|
||||
# Editor / OS
|
||||
.DS_Store
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
# Env (never bake into image)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Git internals
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
Reference in New Issue
Block a user