# ========================================================= # STAGE 1: Hermetic Node Compiler and Code Bundler # ========================================================= FROM node:20-alpine AS build-engine WORKDIR /app # Copy dependency manifests COPY package*.json ./ # FIXED: Swapped 'npm ci' for a clean local-layer 'npm install' # This automatically handles generating full dependency trees dynamically inside Docker RUN npm install # Mount compiler processing environments COPY tailwind.config.js postcss.config.js vite.config.js ./ COPY index.html ./ # Mount asset and source directories COPY public/ ./public/ COPY src/ ./src/ # Compile production build distributions into /app/dist ENV NODE_ENV=production RUN npm run build # ========================================================= # STAGE 2: Hardened, Unprivileged Static Edge Server # ========================================================= FROM nginx:alpine-slim # Enforce system-level wget availability to guarantee health checks survive baseline upgrades RUN apk add --no-cache wget \ && rm -rf /usr/share/nginx/html/* \ && rm -f /etc/nginx/conf.d/default.conf /etc/nginx/nginx.conf # Inject security configurations into global runtime path COPY nginx.conf /etc/nginx/nginx.conf # Extract tree-shaken static assets directly out of Stage 1 dist/ COPY --from=build-engine /app/dist /usr/share/nginx/html # Create and register custom temp structures under explicit non-root contexts RUN mkdir -p /tmp/client_temp /tmp/proxy_temp /tmp/fastcgi_temp /tmp/uwsgi_temp /tmp/scgi_temp \ && chown -R nginx:nginx /usr/share/nginx/html /var/cache/nginx /var/log/nginx \ /tmp/client_temp /tmp/proxy_temp /tmp/fastcgi_temp /tmp/uwsgi_temp /tmp/scgi_temp # Drop all execution privileges cleanly to non-root USER nginx EXPOSE 8080 # Hardened Runtime Health Check evaluating index.html availability securely HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget -qO- http://localhost:8080/index.html >/dev/null || exit 1 CMD ["nginx", "-g", "daemon off;"]