# 🚀 Frontend (Portal) Dockerfile - Plesk Optimized # Multi-stage build for Next.js production deployment via Plesk # ===================================================== # Dependencies Stage - Install all dependencies # ===================================================== FROM node:22-alpine AS deps # Install system dependencies for building RUN apk add --no-cache libc6-compat dumb-init # Install pnpm RUN corepack enable && corepack prepare pnpm@10.15.0 --activate WORKDIR /app # Copy workspace configuration COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./ # Copy package.json files for dependency resolution COPY packages/domain/package.json ./packages/domain/ COPY packages/validation/package.json ./packages/validation/ COPY apps/portal/package.json ./apps/portal/ # Install dependencies with frozen lockfile RUN pnpm install --frozen-lockfile --prefer-offline # ===================================================== # Builder Stage - Build the application # ===================================================== FROM node:22-alpine AS builder # Install pnpm RUN corepack enable && corepack prepare pnpm@10.15.0 --activate WORKDIR /app # Copy workspace configuration COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./ # Copy source code COPY packages/ ./packages/ COPY apps/portal/ ./apps/portal/ COPY tsconfig.json tsconfig.base.json ./ # Ensure public directory exists even if the repo doesn't have one RUN mkdir -p /app/apps/portal/public # Copy node_modules from deps stage COPY --from=deps /app/node_modules ./node_modules # Build shared workspace packages first RUN pnpm --filter @customer-portal/domain build && \ pnpm --filter @customer-portal/validation build # Build portal with standalone output WORKDIR /app/apps/portal ENV NODE_ENV=production RUN pnpm build # ===================================================== # Production Stage - Final optimized image for Plesk # ===================================================== FROM node:22-alpine AS production # Install runtime dependencies including dumb-init for proper signal handling RUN apk add --no-cache \ wget \ curl \ dumb-init \ libc6-compat \ && rm -rf /var/cache/apk/* WORKDIR /app # Create non-root user for security [[memory:6689308]] RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs # Copy the Next.js standalone build with proper ownership COPY --from=builder --chown=nextjs:nodejs /app/apps/portal/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/apps/portal/.next/static ./apps/portal/.next/static COPY --from=builder --chown=nextjs:nodejs /app/apps/portal/public ./apps/portal/public # Create necessary directories and set permissions RUN mkdir -p /app/logs && \ chown -R nextjs:nodejs /app # Switch to non-root user USER nextjs # Expose port (required for Plesk port mapping) EXPOSE 3000 # Environment variables ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" # Health check for container monitoring HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 # Use dumb-init for proper signal handling in containers ENTRYPOINT ["dumb-init", "--"] CMD ["node", "apps/portal/server.js"]