# Stage 1: Dependencies FROM node:20-alpine AS deps RUN npm install -g pnpm@9 WORKDIR /app # Copy workspace configuration and all package.json files COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./ COPY apps/web/package.json ./apps/web/ COPY packages/core/package.json ./packages/core/ COPY packages/db/package.json ./packages/db/ COPY packages/ui/package.json ./packages/ui/ # Install all dependencies RUN pnpm install --frozen-lockfile # Stage 2: Builder FROM node:20-alpine AS builder RUN npm install -g pnpm@9 WORKDIR /app # Copy all source code first COPY . . # Copy node_modules from deps stage (this overwrites the empty directories) COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/apps/web/node_modules ./apps/web/node_modules COPY --from=deps /app/packages/core/node_modules ./packages/core/node_modules COPY --from=deps /app/packages/db/node_modules ./packages/db/node_modules COPY --from=deps /app/packages/ui/node_modules ./packages/ui/node_modules # Set build-time environment variables ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production # Build-time arguments for NEXT_PUBLIC_* variables # These are embedded into the JavaScript bundle at build time ARG NEXT_PUBLIC_APP_URL ARG NEXT_PUBLIC_API_URL ARG NEXT_PUBLIC_SENTRY_DSN ARG NEXT_PUBLIC_SENTRY_RELEASE ARG NEXT_PUBLIC_OPENPANEL_CLIENT_ID ARG NEXT_PUBLIC_OPENPANEL_API_URL ARG NEXT_PUBLIC_OPENPANEL_SCRIPT_URL ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID ARG NEXT_PUBLIC_UMAMI_URL ARG NEXT_PUBLIC_IS_PRIMARY # Convert ARGs to ENVs so they're available during build ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN ENV NEXT_PUBLIC_SENTRY_RELEASE=$NEXT_PUBLIC_SENTRY_RELEASE ENV NEXT_PUBLIC_OPENPANEL_CLIENT_ID=$NEXT_PUBLIC_OPENPANEL_CLIENT_ID ENV NEXT_PUBLIC_OPENPANEL_API_URL=$NEXT_PUBLIC_OPENPANEL_API_URL ENV NEXT_PUBLIC_OPENPANEL_SCRIPT_URL=$NEXT_PUBLIC_OPENPANEL_SCRIPT_URL ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID ENV NEXT_PUBLIC_UMAMI_URL=$NEXT_PUBLIC_UMAMI_URL ENV NEXT_PUBLIC_IS_PRIMARY=$NEXT_PUBLIC_IS_PRIMARY # Build the application (turbo will build dependencies first) RUN pnpm turbo run build --filter=@skillhub/web # Stage 3: Runner FROM node:20-alpine AS runner WORKDIR /app # Install curl for health checks RUN apk add --no-cache curl ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 # Create non-root user RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs # Copy built application (monorepo standalone preserves directory structure) COPY --from=builder /app/apps/web/public ./apps/web/public COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static # Copy messages folder for internationalization (next-intl requires this at runtime) COPY --from=builder --chown=nextjs:nodejs /app/apps/web/messages ./apps/web/messages USER nextjs EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" CMD ["node", "apps/web/server.js"]