# ── Stage 1: build ──────────────────────────────────────────────────────────── FROM node:20-alpine AS builder # Create non-root user (UID/GID 1001) RUN addgroup -g 1001 appuser && adduser -u 1001 -G appuser -s /bin/sh -D appuser WORKDIR /app RUN chown appuser:appuser /app USER appuser COPY --chown=appuser:appuser package.json package-lock.json* ./ RUN npm ci COPY --chown=appuser:appuser . . RUN npm run build # ── Stage 2: serve with nginx (unprivileged, UID 1001) ──────────────────────── FROM nginxinc/nginx-unprivileged:alpine # nginx-unprivileged already sets USER nginx (101). Step up to root only for # user creation, then drop back. All nginx writable paths go through /tmp # (world-writable) so appuser can run the server without extra chowns. USER root RUN addgroup -g 1001 appuser && adduser -u 1001 -G appuser -D appuser USER appuser COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 8080