# syntax=docker/dockerfile:1.7 # # Multi-stage build for the helix-engage sidecar. # # Why multi-stage instead of "build on host, COPY dist + node_modules"? # The host (developer Mac, CI runner) is rarely the same architecture # as the target (linux/amd64 EC2 / VPS). Copying a host-built # node_modules brings darwin-arm64 native bindings (sharp, livekit, # fsevents, etc.) into the runtime image, which crash on first import. # This Dockerfile rebuilds inside the target-platform container so # native bindings are downloaded/compiled for the right arch. # # The build stage runs `npm ci` + `nest build`, then `npm prune` to # strip dev deps. The runtime stage carries forward only `dist/`, # the pruned `node_modules/`, and `package.json`. # --- Builder stage ---------------------------------------------------------- FROM node:22-slim AS builder WORKDIR /app # Build deps for any native modules whose prebuilt binaries miss the # target arch. Kept minimal — node:22-slim already ships most of what's # needed for the deps in this project, but python/make/g++ are the # canonical "I might need to gyp-rebuild" trio. RUN apt-get update \ && apt-get install -y --no-install-recommends \ python3 \ make \ g++ \ && rm -rf /var/lib/apt/lists/* # Lockfile-only install first so this layer caches when only source # changes — much faster repeat builds. COPY package.json package-lock.json ./ RUN npm ci --no-audit --no-fund --loglevel=verbose # Source + build config COPY tsconfig.json tsconfig.build.json nest-cli.json ./ COPY src ./src RUN npm run build # Strip dev dependencies so the runtime image stays small. RUN npm prune --omit=dev # --- Runtime stage ---------------------------------------------------------- FROM node:22-slim WORKDIR /app # Bring across only what the runtime needs. Source, dev deps, build # tooling all stay in the builder stage and get discarded. COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./ EXPOSE 4100 CMD ["node", "dist/main.js"]