38 lines
1.1 KiB
Docker
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;"]
|