# syntax=docker/dockerfile:1.6 # Multi-stage build: install + build the client and server, then run a slim # node image that serves both from one process on $PORT (default 3000). # ─── Stage 1: deps + build ────────────────────────────────────────────────── FROM node:22-bookworm-slim AS builder WORKDIR /app # Root deps COPY package.json package-lock.json* ./ RUN --mount=type=cache,target=/root/.npm npm ci --no-audit --no-fund # Client deps COPY client/package.json client/package-lock.json* ./client/ RUN --mount=type=cache,target=/root/.npm npm --prefix client ci --no-audit --no-fund # Source COPY tsconfig.json ./ COPY server ./server COPY client ./client # Build client (vite → client/dist) and server (tsc → dist/server) RUN npm run build # ─── Stage 2: runtime ─────────────────────────────────────────────────────── FROM node:22-bookworm-slim AS runtime ENV NODE_ENV=production ENV PORT=3000 WORKDIR /app # Only ship the prod deps + built artefacts COPY package.json package-lock.json* ./ RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev --no-audit --no-fund \ && npm cache clean --force COPY --from=builder /app/dist ./dist COPY --from=builder /app/client/dist ./client/dist EXPOSE 3000 CMD ["node", "dist/server/index.js"]