A Nest multi-stage image stays reliable when the builder alone runs install plus nest build (and any prisma generate / native compile), then the runtime stage copies only dist, the pruned production node_modules, and the lockfile-derived tree — never the compiler, never a leftover .npm cache. Match libc between stages (glibc vs musl) so bcrypt-style .node addons do not fail at ERR_DLOPEN after a “green” build. Listen on a private interface or Compose network only, run as non-root, set NODE_ENV=production, and fail the image build if a required native binding is missing for the runtime arch rather than discovering it on first boot behind a proxy.
After rebuild I exec into the runtime container and require the native module once, then hit a readiness route that boots Nest plus a trivial crypto or DB ping from an adjacent proxy container on the internal network, and finally the same path through published TLS. A short 502 with a healthy container almost always means wrong internal port, missing network membership, or the process still compiling on start — move generation into the builder and keep start as node dist/main. Log upstream status beside request time at the edge so a crash-looping Nest worker is not mistaken for TLS or vhost drift.
A Nest multi-stage image stays reliable when the builder alone runs install plus nest build (and any prisma generate / native compile), then the runtime stage copies only dist, the pruned production node_modules, and the lockfile-derived tree — never the compiler, never a leftover .npm cache. Match libc between stages (glibc vs musl) so bcrypt-style .node addons do not fail at ERR_DLOPEN after a “green” build. Listen on a private interface or Compose network only, run as non-root, set NODE_ENV=production, and fail the image build if a required native binding is missing for the runtime arch rather than discovering it on first boot behind a proxy.
After rebuild I exec into the runtime container and require the native module once, then hit a readiness route that boots Nest plus a trivial crypto or DB ping from an adjacent proxy container on the internal network, and finally the same path through published TLS. A short 502 with a healthy container almost always means wrong internal port, missing network membership, or the process still compiling on start — move generation into the builder and keep start as node dist/main. Log upstream status beside request time at the edge so a crash-looping Nest worker is not mistaken for TLS or vhost drift.