Files
mimic-big/frontend/Dockerfile
knacky 649194b174
Some checks failed
ci / backend (lint + typecheck + unit tests) (push) Failing after 1s
ci / frontend (lint + typecheck + build + unit tests) (push) Failing after 0s
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
2026-05-22 19:59:09 +02:00

38 lines
1.1 KiB
Docker

# syntax=docker/dockerfile:1.7
# --- Stage 1: build --------------------------------------------------------
FROM node:22-alpine AS build
ENV CI=true \
npm_config_audit=false \
npm_config_fund=false
WORKDIR /app
# Reproducible install: lockfile only, no scripts at install time.
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
# App sources.
COPY . .
# Production build → /app/dist
RUN npm run build
# --- Stage 2: runtime ------------------------------------------------------
# nginxinc/nginx-unprivileged is the upstream-maintained variant that runs
# nginx as a non-root user out of the box (no chown gymnastics, /var/cache
# /var/run/nginx are owned by uid 101). It already listens on 8080.
FROM docker.io/nginxinc/nginx-unprivileged:alpine
# Minimal SPA serving config: static dist + try_files SPA fallback.
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Static assets owned by the nginx user (uid 101 in this image).
COPY --from=build --chown=101:101 /app/dist /usr/share/nginx/html
EXPOSE 8080
# Foreground nginx so the container lifecycle matches.
CMD ["nginx", "-g", "daemon off;"]