# ============================================================================== # EpicNext-CMS — Docker image (Node 26.8.1, pnpm 11.24.0, Next.js standalone) # ============================================================================== # --- Builder stage --- FROM node:26.8.1-bookworm-slim AS builder # pnpm is required and pinned in package.json (packageManager: pnpm@11.24.0). # Node 26 does not bundle corepack anymore, so install pnpm via npm. RUN npm install -g pnpm@11.24.0 WORKDIR /app # First copy only the manifests so dependency layers are cached. COPY pnpm-lock.yaml package.json pnpm-workspace.yaml .npmrc ./ RUN pnpm install --frozen-lockfile --ignore-scripts # Copy the rest of the source. COPY . . # Build the production bundle. ENV NODE_ENV=production RUN pnpm build # Copy runtime dependencies (node_modules) needed by standalone output. RUN pnpm prune --prod # --- Runtime stage --- FROM node:26.8.1-bookworm-slim AS runner ENV NODE_ENV=production ENV PORT=3002 ENV HOSTNAME=0.0.0.0 # Occupied base port on the host; keep PM2-style default. EXPOSE 3002 # Non-root user for security. RUN groupadd --system --gid 1001 nodejs \ && useradd --system --uid 1001 --gid nodejs nextjs WORKDIR /app # Storage directory for runtime uploaded media (persistent volume). # nitro/swf client assets (~3GB) are mounted here as volumes at runtime. RUN mkdir -p /app/storage \ /app/public/nitro-assets \ /app/public/swf \ && chown -R nextjs:nodejs /app # Copy standalone Next.js output (includes a minimal node_modules). COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ # Copy static assets (public files served directly). COPY --from=builder --chown=nextjs:nodejs /app/public ./public # Copy the server-side static build output. COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Client assets + runtime uploads live outside the image (mounted volumes). VOLUME ["/app/public/nitro-assets", "/app/public/swf", "/app/storage"] USER nextjs CMD ["node", "server.js"]