Photo by Rubaitul Azad / Unsplash

Building smaller secure docker images with Multi-Stage builds

docker Apr 10, 2019

Docker v17.05 introduced multi-stage builds giving us more ways to package and secure our applications. This post will show you how to use the power of the multi-stage builds for your projects. Check out the below Dockerfile and try to guess what we are trying to achieve here. We will go through each of the steps in detail right after that.

# Step 1
FROM node:10-alpine AS base
WORKDIR /app
COPY package.json .

# Step 2
FROM base AS dependencies
RUN npm install --only=production
RUN cp -R node_modules prod_node_modules
RUN npm install

# Step 3
FROM dependencies AS build
COPY . .
RUN npm run build

# Step 4
FROM base AS final
COPY --from=dependencies /app/prod_node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/entrypoint entrypoint
RUN chmod +x entrypoint
EXPOSE 3000
ENTRYPOINT [ "sh", "entrypoint" ]

Now let us go through the multiple stages we have used to create our final image -

  1. Taking node:10-alpine image and naming it as base so that we can use it later in our build stage. This build stage will have only basic instructions. Here we have mentioned where our working directory will be an added our node projects package.json file to the working directory.
  2. We now take the base image and start building our dependencies image. Firstly we install only production dependencies then copy the node_modules directory to prod_node_modules directory. Then we install our dev dependencies on top of it. Step 4 will unravel why we did this.
  3. We then take the dependencies image and start building our build image. We copy all the files of our project to the working directory and start compiling/building our project. In this case, the source is written in Typescript and we are compiling it to Javascript.

Now we pick only required directories for our final image. Which includes -

  • copying production node_modules from our dependencies image.
  • copying our compiled/built code form build image.
  • copying our entrypoint script from build image and giving it appropriate permissions.
  • exposing the port for the application.
  • adding the entrypoint. In this case, it is sh entrypoint.

Pros

The benefits of using multi-stage builds are -

  • only the necessary files are kept in the final image. In this case, source files are not copied when we prepare the final image. This reduces the size of image.
  • small image sizes reduce the deployment time.
  • keeping the files and dependencies to the minimum improves security.

Hope you learned something new today!

Related reads

Use multi-stage builds - https://docs.docker.com/develop/develop-images/multistage-build

Tags