1001Ferramentas
🐳 Dev

Multistage Dockerfile Builder

Generates a multistage Dockerfile (builder + runtime) for Node.js apps — copies node_modules only from the builder.

Why a multi-stage build keeps the image small

A carelessly built Docker image carries everything used to build it: compiler, development dependencies, package manager cache, source code. That bloats the image, lengthens deployment and widens the attack surface — every extra tool in there is another tool in the hands of anyone who gets inside the container.

A multi-stage build cuts that off at the root. A first stage, the builder, installs everything and compiles. A second stage starts from a clean image and copies only the result, with COPY --from=builder. Everything left behind in the first stage is discarded and never reaches the final image. Enter the Node version and the page generates the Dockerfile with both stages already wired up.

The order of instructions in the generated file is not accidental. package.json is copied and dependencies installed before the rest of the code, because each instruction becomes a layer with its own cache: changing one line of source does not invalidate the npm ci layer, and the next build reuses what was already there. Swapping those two lines makes the whole install run again on every change.

Frequently asked questions

How many stages can I have?
As many as you need. Three is common: one to install dependencies, one to compile and a final one holding only the result. You can also copy from a stage that is not the immediately preceding one, and even from an external image, with COPY --from=nginx:alpine.
Why npm ci rather than npm install?
ci installs exactly what package-lock.json specifies, updating nothing, and fails if the lock is out of sync with package.json. In a build that is what you want: an identical result on every run. install may alter the lock, which makes the image non-reproducible.
Does the final image have to match the builder?
No, and often it does not. The pattern generated here keeps Node because the application needs it at runtime. For a compiled binary — Go, Rust — the final stage can be a distroless image or even scratch, with no shell and no package manager, cutting the image down to a few megabytes.

Related Tools