Multi-stage builds is a feature that was added in Docker v17.05. It allows you to use multiple FROM instructions to define multiple images as stages inside a single Dockerfile.
You can extract artifacts from the previous stage and add them to the next stage in a single instruction (and thus a single layer).
In our case, we can define two stages – one for building our application, and the second one that just copies the dist and node_modules directory and specifies the CMD instruction:
FROM node:8-alpine as builder
USER node
WORKDIR /home/node
COPY --chown=node:node . .
RUN ["yarn"]
COPY --chown=node:node . .
RUN ["yarn", "run", "build"]
RUN find . ! -name dist ! -name node_modules -maxdepth 1 -mindepth 1 -exec rm -rf {} \;
FROM node:8-alpine
USER node
WORKDIR /home/node
COPY --chown=node:node --from=builder /home/node .
CMD ["node", "dist/index.js"]
We use the as keyword to name our stage, and refer to them in the COPY instructions using the --from flag.
Now, if we build this using Dockerfile, we end up with two images:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hobnob 0.1.0 5268f2a4176b 5 seconds ago 122MB
<none> <none> f722d00c2dbf 9 seconds ago 210MB
The one without a name, <none>, represents the first stage, and the hobnob:0.1.0 image is the second stage. As you can see, our image is now only 122 MB, but we still benefited from our multi-layer Dockerfile and caching.