# Build context: ./out (turbo prune storefront --docker) # out/json/ — package.json files only → used by deps stage for layer caching # out/full/ — full pruned monorepo → used by builder stage for source # out/package-lock.json # ── Stage 1: deps ──────────────────────────────────────────────────────────── # Install ALL dependencies (dev + prod) using only the package.json tree. # This layer is shared with the builder stage and only rebuilds when # a package.json or the lock file changes — not when source code changes. FROM node:20-alpine AS deps RUN apk add --no-cache libc6-compat WORKDIR /app # Upgrade npm to match the project's packageManager (npm@11). The package-lock.json # was generated with npm 11 — npm 10 (bundled with node:20) can't fully parse it, # causing turbo prune to generate an incomplete pruned lockfile and npm ci to miss # packages like @heroui/react. RUN npm install -g npm@11 --quiet COPY json/ . COPY package-lock.json . RUN npm ci # ── Stage 2: builder ───────────────────────────────────────────────────────── # Full monorepo source + build artifact. # next build produces .next/standalone/ because output: "standalone" is set # in next.config.js — that's what makes the runner stage small. FROM node:20-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY full/ . # NEXT_PUBLIC_* vars are baked into the client bundle at build time by Next.js. # They must be present here (not just at runtime) or SSG/prerender fails with # "Missing publishableKey". Pass via --build-arg in CI. ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY ARG NEXT_PUBLIC_CONVEX_URL ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY \ NEXT_PUBLIC_CONVEX_URL=$NEXT_PUBLIC_CONVEX_URL \ NEXT_TELEMETRY_DISABLED=1 RUN npx turbo build --filter=storefront # ── Stage 3: runner ────────────────────────────────────────────────────────── # Minimal runtime image — only the standalone bundle, static assets, and public dir. # No source code, no dev dependencies, no build tools. FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ HOSTNAME=0.0.0.0 \ PORT=3000 # Non-root user for security RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001 # standalone output mirrors the monorepo tree, so server.js lands at /app/server.js # Static files and public/ must be copied separately — they are not in standalone/ COPY --from=builder --chown=nextjs:nodejs /app/apps/storefront/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/apps/storefront/.next/static ./apps/storefront/.next/static COPY --from=builder --chown=nextjs:nodejs /app/apps/storefront/public ./apps/storefront/public USER nextjs EXPOSE 3000 CMD ["node", "server.js"]