Starting from , you can create a single that can build multiple helper images with compilers, , and tests and use files from above images to produce the Docker image. Docker 17.05+ Dockerfile tools final The “core principle” of Dockerfile Docker can build images by reading the instructions from a . A is a text file that contains a list of all the commands needed to build a new Docker image. The syntax of is pretty simple and the Docker team tries to keep it intact between Docker engine releases. Dockerfile Dockerfile Dockerfile The core principle is very simple: . 1 Dockerfile -> 1 Docker Image This principle works just fine for basic use cases, where you just need to demonstrate Docker capabilities or put some “static” content into a Docker image. Once you advance with Docker and would like to create secure and lean Docker images, a single is not enough. Dockerfile People who insist on following the above principle find themselves with slow Docker builds, huge Docker images (several GB size images), slow deployment time and lots of CVE violations embedded into these images. The Docker Build Container pattern Docker Pattern: The Build Container The basic idea behind pattern is simple: Build Container Create additional Docker images with required tools (compilers, linters, testing tools) and use these images to produce lean, secure and production ready Docker image. An example of the pattern for typical Node.js application: Build Container Derive a Node base image (for example ) and installed ( ) FROM node:6.10-alpine node npm Dockerfile.build Add package.json Install all node modules from and dependency devDependency Copy application code Run compilers, code coverage, linters, code analysis and testing tools Create the Docker image; derive same or other Node base image production FROM install node modules required for runtime ( ) npm install --only=production expose and define a default (command to run your application) PORT CMD Push the image to some Docker registry production This flow assumes that you are using two or more s and a shell script or flow tool to orchestrate all steps above. Dockerfile Example I use a fork of node.js application. Let’s Chat Builder Docker image with eslint, mocha and gulp FROM alpine:3.5# install nodeRUN apk add --no-cache nodejs# set working directoryWORKDIR /root/chat# copy project fileCOPY package.json .# install node packagesRUN npm set progress=false && \npm config set depth 0 && \npm install# copy app filesCOPY . .# run linter, setup and testsCMD npm run lint && npm run setup && npm run test Production Docker image with ‘production’ node modules only FROM alpine:3.5 # install nodeRUN apk add --no-cache nodejs tini# set working directoryWORKDIR /root/chat# copy project fileCOPY package.json .# install node packagesRUN npm set progress=false && \npm config set depth 0 && \npm install --only=production && \npm cache clean# copy app filesCOPY . .# Set tini as entrypointENTRYPOINT [“/sbin/tini”, “--”]# application server portEXPOSE 5000# default run commandCMD npm run start What is Docker multi-stage build? Docker 17.0.5 extends syntax to support new build, by extending two commands: and . Dockerfile multi-stage FROM COPY The build allows using multiple commands in the same Dockerfile. The last command produces the final Docker image, all other images are intermediate images (no final Docker image is produced, but ). multi-stage FROM FROM all layers are cached The syntax also supports keyword. Use keyword to give the current image a logical name and reference to it later by this name. FROM AS AS To copy files from intermediate images use , where number starts from (but better to use logical name through keyword). COPY --from=<image_AS_name|image_number> 0 AS Creating a multi-stage Dockerfile for Node.js application The below makes the pattern obsolete, allowing to achieve the same result with the single file. Dockerfile Build Container # ---- Base Node ----FROM alpine:3.5 AS base# install nodeRUN apk add --no-cache nodejs-npm tini# set working directoryWORKDIR /root/chat# Set tini as entrypointENTRYPOINT ["/sbin/tini", "--"]# copy project fileCOPY package.json . # ---- Dependencies ----FROM base AS dependencies# install node packagesRUN npm set progress=false && npm config set depth 0RUN npm install --only=production# copy production node_modules asideRUN cp -R node_modules prod_node_modules# install ALL node_modules, including 'devDependencies'RUN npm install # ---- Test ----# run linters, setup and testsFROM dependencies AS testCOPY . .RUN npm run lint && npm run setup && npm run test # ---- Release ----FROM base AS release# copy production node_modulesCOPY --from=dependencies /root/chat/prod_node_modules ./node_modules# copy app sourcesCOPY . .# expose port and define CMDEXPOSE 5000CMD npm run start The above creates 3 intermediate Docker images and single Docker image (the final ). Dockerfile release FROM First image – is a base Node image with: , , (init app) and FROM alpine:3.5 AS base node npm tini package.json Second image – contains all node modules from and with additional copy of required for final image only FROM base AS dependencies dependencies devDependencies dependencies Third image – runs linters, setup and tests (with ); if this run command fail not final image is produced FROM dependencies AS test mocha The final image – is a base Node image with application code and all node modules from FROM base AS release dependencies Try Docker multi-stage build today In order to try Docker build, you need to get Docker 17.0.5, which is going to be released in May and currently available on the channel. multi-stage beta So, you have two options: Use channel to get Docker 17.0.5 beta Run container (docker-in-docker) dind Running Docker-in-Docker 17.0.5 (beta) Running Docker 17.0.5 (beta) in docker container ( is required): --privileged $ docker run -d --rm --privileged -p 23751:2375 --name dind \docker:17.05.0-ce-dind --storage-driver overlay2 Try build. Add to every Docker command, or set environment variable. mult-stage --host=:23751 DOCKER_HOST $ # using --host$ docker --host=:23751 build -t local/chat:multi-stage . $ # OR: setting DOCKER_HOST$ export DOCKER_HOST=localhost:23751$ docker build -t local/chat:multi-stage . Summary With Docker build feature, it’s possible to implement an advanced Docker image build pipeline using a single Dockerfile . multi-stage to Docker team for such a useful feature! Kudos Hope, you find this post useful. I look forward to your comments and any questions you have. Originally published at codefresh.io on April 24, 2017.