# ============================================================================= # STAGE 1: Dependencies - Install only what the API needs # ============================================================================= FROM oven/bun:1.4.4 AS deps WORKDIR /app # Copy root workspace config. bunfig.toml keeps the hoisted linker: with this # lockfile (configVersion 1), Bun 1.3 and later default workspaces to isolated # installs, and the builder stage copies only the root node_modules. COPY package.json bun.lock bunfig.toml ./ # Strip root package.json to only keep workspaces config. # The root has frontend deps (design-system, react-dnd, sharp, semantic-release, etc.) # that the API doesn't need. Removing them cuts ~800 packages from the install. RUN cat package.json | bun -e " \ const pkg = JSON.parse(await Bun.stdin.text()); \ delete pkg.dependencies; delete pkg.devDependencies; delete pkg.scripts; \ console.log(JSON.stringify(pkg, null, 2));" > package.min.json \ && mv package.min.json package.json # Copy only the workspace package.json files the API depends on COPY packages/auth/package.json ./packages/auth/ COPY packages/db/package.json ./packages/db/ COPY packages/utils/package.json ./packages/utils/ COPY packages/integration-platform/package.json ./packages/integration-platform/ COPY packages/tsconfig/package.json ./packages/tsconfig/ COPY packages/email/package.json ./packages/email/ COPY packages/company/package.json ./packages/company/ COPY packages/billing/package.json ./packages/billing/ # Copy API package.json COPY apps/api/package.json ./apps/api/ # Install dependencies — skip lifecycle scripts (husky, etc. not needed in Docker) RUN bun install --ignore-scripts # ============================================================================= # STAGE 2: Builder - Build workspace packages and NestJS app # ============================================================================= FROM oven/bun:1.4.4 AS builder # Run the Nest CLI on Node.js, not on the image's Bun `node` shim. Under Bun # 1.4, require.resolve applies tsconfig paths, so `nest build` takes `@db` for # an installed package and leaves the alias unrewritten in dist/. COPY --from=node:22.23.4-slim /usr/local/bin/node /usr/local/bin/node WORKDIR /app # Copy node_modules first (from deps stage), then source on top. # This avoids conflicts between workspace symlinks and local node_modules # that get included from the build context. COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/package.json ./package.json COPY --from=deps /app/bun.lock ./bun.lock # Copy workspace packages source (node_modules excluded by .dockerignore, # but COPY replaces directories so workspace node_modules from deps get wiped) COPY packages/auth ./packages/auth COPY packages/db ./packages/db COPY packages/utils ./packages/utils COPY packages/integration-platform ./packages/integration-platform COPY packages/tsconfig ./packages/tsconfig COPY packages/email ./packages/email COPY packages/company ./packages/company COPY packages/billing ./packages/billing # Copy API source COPY apps/api ./apps/api # Build db — generates Prisma client + compiles dist/ for @trycompai/db consumers RUN cd packages/db && bun run build # Build remaining workspace packages RUN cd packages/auth && bun run build \ && cd ../integration-platform && bun run build \ && cd ../email && bun run build \ && cd ../company && bun run build \ && cd ../billing && bun run build # Copy model files to api schema dir, then build NestJS app # Note: @prisma/client is already generated by packages/db build (generate-prisma-client-js.js) RUN find /app/packages/db/prisma/schema -name '*.prisma' ! -name 'schema.prisma' -exec cp {} /app/apps/api/prisma/schema/ \; \ && cd /app/apps/api && bunx nest build # ============================================================================= # STAGE 3: Production Runtime # ============================================================================= FROM node:22.23.4-slim AS production # Create non-root user before copying files so COPY --chown can use it RUN groupadd --system nestjs && useradd --system --gid nestjs --create-home nestjs WORKDIR /app RUN chown nestjs:nestjs /app # Install runtime dependencies + AWS RDS CA certificate bundle. # fontconfig + fonts-dejavu-core are needed so sharp/librsvg can render text # in the browser-automation screenshot overlay (node:*-slim has no fonts). RUN apt-get update && apt-get install -y --no-install-recommends \ wget ca-certificates fontconfig fonts-dejavu-core \ && wget -q -O /usr/local/share/aws-rds-ca-bundle.pem https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem \ && apt-get clean && rm -rf /var/lib/apt/lists/* ENV NODE_EXTRA_CA_CERTS=/usr/local/share/aws-rds-ca-bundle.pem # Copy built NestJS app COPY --from=builder --chown=nestjs:nestjs /app/apps/api/dist ./dist # Copy prisma schema (for reference only — client is already generated in node_modules) COPY --from=builder --chown=nestjs:nestjs /app/apps/api/prisma ./prisma # Copy package.json (for any runtime needs) COPY --from=builder --chown=nestjs:nestjs /app/apps/api/package.json ./package.json # Copy workspace packages that are referenced by node_modules symlinks COPY --from=builder --chown=nestjs:nestjs /app/packages/auth ./packages/auth COPY --from=builder --chown=nestjs:nestjs /app/packages/db ./packages/db COPY --from=builder --chown=nestjs:nestjs /app/packages/utils ./packages/utils COPY --from=builder --chown=nestjs:nestjs /app/packages/integration-platform ./packages/integration-platform COPY --from=builder --chown=nestjs:nestjs /app/packages/tsconfig ./packages/tsconfig COPY --from=builder --chown=nestjs:nestjs /app/packages/email ./packages/email COPY --from=builder --chown=nestjs:nestjs /app/packages/company ./packages/company COPY --from=builder --chown=nestjs:nestjs /app/packages/billing ./packages/billing # Copy production node_modules (includes Prisma client already generated for linux/amd64) COPY --from=builder --chown=nestjs:nestjs /app/node_modules ./node_modules # Set production environment ENV NODE_ENV=production ENV PORT=3333 USER nestjs EXPOSE 3333 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3333/v1/health || exit 1 # Start the application with crash diagnostics CMD ["node", "--report-on-fatalerror", "--report-compact", "dist/src/main.js"]