# ---- Stage 1: build shared + client (static assets) ---- FROM node:20-bookworm-slim AS build-client WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* COPY package.json ./ COPY shared/package.json shared/package.json COPY server/package.json server/package.json COPY client/package.json client/package.json RUN npm install COPY shared shared COPY client client COPY .git .git RUN npm run build -w shared && npm run build -w client # ---- Stage 2: build shared + server (compiled JS) ---- FROM node:20-bookworm-slim AS build-server WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* COPY package.json ./ COPY shared/package.json shared/package.json COPY server/package.json server/package.json COPY client/package.json client/package.json RUN npm install COPY shared shared COPY server server COPY .git .git RUN npm run build -w shared && npm run build -w server # ---- Stage 3: runtime ---- # node:20-bookworm-slim (glibc) is used instead of alpine so that better-sqlite3 and argon2's # native bindings can use prebuilt binaries rather than compiling from source. FROM node:20-bookworm-slim AS runtime WORKDIR /app ENV NODE_ENV=production COPY package.json ./ COPY shared/package.json shared/package.json COPY server/package.json server/package.json COPY client/package.json client/package.json RUN npm install --omit=dev --workspace=shared --workspace=server COPY --from=build-server /app/shared/dist shared/dist COPY --from=build-server /app/server/dist server/dist COPY --from=build-client /app/client/dist client/dist EXPOSE 3000 CMD ["node", "server/dist/index.js"]