# ═══════════════════════════════════════════════════════════════════════════ # Stage 1 – deps: install node_modules with full cache mount # (This layer is only re-run when package.json / package-lock.json changes) # ═══════════════════════════════════════════════════════════════════════════ FROM node:20-alpine AS deps RUN apk add --no-cache libc6-compat WORKDIR /app COPY package.json package-lock.json* ./ # --mount=type=cache keeps the npm cache on the host between builds # --prefer-offline avoids network fetches when the package is already cached RUN --mount=type=cache,target=/root/.npm \ npm ci --prefer-offline # ═══════════════════════════════════════════════════════════════════════════ # Stage 2 – builder: compile Next.js with build-cache mount for turbo-speed # ═══════════════════════════════════════════════════════════════════════════ FROM node:20-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production ARG NEXT_PUBLIC_API_URL=http://backend:8000 ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} # Mount the Next.js build cache so incremental compilation is reused RUN --mount=type=cache,target=/app/.next/cache \ npm run build # ═══════════════════════════════════════════════════════════════════════════ # Stage 3 – runner: minimal production image (no node_modules, no sources) # ═══════════════════════════════════════════════════════════════════════════ FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs # Copy only the standalone server output (no full node_modules) COPY --from=builder /app/public ./public RUN mkdir .next && chown nextjs:nodejs .next COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" CMD ["node", "server.js"]