# 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/ ./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: Build frontend FROM deps AS frontend-builder COPY frontend/ ./frontend/ RUN pnpm --filter pp-frontend run build # Stage 3: Build backend + Prisma client FROM deps AS backend-builder COPY backend/ ./backend/ RUN pnpm --filter pp-backend run generate && \ pnpm --filter pp-backend run build # Stage 4: Minimal production runner FROM node:24-alpine AS runner WORKDIR /app RUN apk add --no-cache openssl # Root node_modules contains prisma CLI and hoisted deps; backend node_modules has # the backend-specific symlinks. Both are needed at runtime. COPY --from=backend-builder /app/node_modules ./node_modules COPY --from=backend-builder /app/backend/dist ./backend/dist COPY --from=backend-builder /app/backend/node_modules ./backend/node_modules COPY --from=backend-builder /app/backend/package.json ./backend/package.json COPY --from=frontend-builder /app/frontend/dist ./frontend/dist COPY prisma/ ./prisma/ WORKDIR /app/backend EXPOSE 5000 CMD sh -c "../node_modules/.bin/prisma migrate deploy --schema=../prisma/schema.prisma && node dist/index.js"