# Stage 1: Install all workspace dependencies # Using workspace-level install so pnpm-workspace.yaml settings (onlyBuiltDependencies, # supply-chain policies, etc.) apply uniformly — the canonical pnpm monorepo pattern. FROM node:24-alpine AS deps WORKDIR /app # Copy workspace manifest files first for layer caching COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY frontend/package.json ./frontend/ COPY backend/package.json ./backend/ COPY prisma/schema.prisma ./prisma/schema.prisma # corepack activates the exact pnpm version declared in package.json#packageManager. # This is hash-verified (sha1) and prevents newer pnpm from applying different # supply-chain policies (e.g. minimumReleaseAge) against a lockfile generated with # the pinned version. RUN corepack enable && corepack install && \ pnpm install --frozen-lockfile # Stage 2: Prune runtime dependencies FROM deps AS prod-deps COPY prisma/ ./prisma/ RUN CI=true pnpm prune --prod # Stage 3: Build frontend FROM deps AS frontend-builder COPY frontend/ ./frontend/ RUN pnpm --filter pp-frontend run build # Stage 4: Build backend + Prisma client FROM deps AS backend-builder COPY backend/ ./backend/ COPY prisma/ ./prisma/ RUN pnpm --filter pp-backend run generate && \ pnpm --filter pp-backend run build:prod # Stage 5: Minimal production runner FROM node:24-alpine AS runner WORKDIR /app RUN apk add --no-cache openssl ENV NODE_PATH=/app/node_modules/.pnpm/node_modules COPY --from=prod-deps /app/node_modules ./node_modules COPY --from=prod-deps /app/backend/node_modules ./backend/node_modules COPY --from=prod-deps /app/backend/package.json ./backend/package.json COPY --from=backend-builder /app/backend/dist ./backend/dist COPY --from=frontend-builder /app/frontend/dist ./frontend/dist COPY prisma/ ./prisma/ WORKDIR /app/backend EXPOSE 5000 CMD sh -c "../node_modules/.pnpm/node_modules/.bin/prisma migrate deploy --schema=../prisma/schema.prisma && node dist/index.js"