# 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;"]