Multi-stage Image Builds with Docker
Tags: docker, devops, tutorial, containers
Published: 2020-04-19 23:55:44

---
title: Multi-stage Image Builds with Docker
published: true
tags: docker, devops, tutorial, containers
cover_image: https://sahansera.dev/static/a2ec3a81c96e93b126bf99f312ed5e5e/2458c/docker-cover.webp
canonical_url: https://sahansera.dev/multi-stage-image-builds-with-docker/
---

### What and Why Behind Multistage Builds

In a Dockerfile, each statement adds up a new layer to the image. It could be counterproductive if you also build your application package when building your Docker image. This could increase the size of our docker images substantially. That’s when we need to leverage multi-stage builds feature.

Simply put, multistage builds are useful when we want to clean up and reduce the image size without keeping unwanted artefacts lying around in our image.

Let’s understand this through an example.

### A Concrete Example

I have created a [sample React app](https://github.com/sahan91/docker-multi-stage-builds) and 2 Dockerfiles to demonstrate this, namely, `Dev.Dockerfile` and `Prod.Dockerfile`

Typical Dockerfile without stages

[Dev.Dockerfile](https://github.com/sahan91/docker-multi-stage-builds/blob/master/Dev.Dockerfile)

```docker
FROM node:13.13.0-alpine
WORKDIR /app

COPY package.json ./
RUN npm install --silent
RUN npm install react-scripts@3.4.1 -g --silent
COPY . ./

CMD ["npm", "start"]
```

Let’s build and run this Docker image:

```bash
docker build -t multi-stage-demo:dev -f Dev.Dockerfile .
```

In the lightweight version, we use the same base image to build the application, but we are using `nginx` base image to run our application since we already have our application built.

[Prod.Dockerfile](https://github.com/sahan91/docker-multi-stage-builds/blob/master/Prod.Dockerfile)

```docker
# Stage 1 - the build process
FROM node:13.13.0-alpine as build-deps
WORKDIR /app

COPY package.json ./
RUN npm install --silent
RUN npm install react-scripts@3.4.1 -g --silent
COPY . ./
RUN npm run build

# Stage 2 - the deploy process
FROM nginx
COPY --from=build-deps /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

Here, you’d notice that we have two `FROM` directives. The first one will be used for the base image for building the application and the second one for the final image. The built binaries are copied over from the first image over to the second and we expose port 80 to the host.

Now, let’s give this a go and build it:

```bash
docker build -t multi-stage-demo:prod -f Prod.Dockerfile .
```

Time to check our image sizes

```bash
docker images | grep multi-stage-demo
```

![Untitled.png](https://sahansera.dev/static/d05c43957685371320d93ce0d194306f/11d19/Untitled.png)

If we compare `dev` to `prod` image, that’s a **~73% reduction**! 😍This can also benefit you in reduced deployment times.

The Github repo with code is available [here](https://github.com/sahan91/docker-multi-stage-builds)

### Where to From Here?

There are some nice examples and best practices defined in the official Docker documentation.

> For example, if your build contains several layers, you can order them from the less frequently changed (to ensure the build cache is reusable) to the more frequently changed:
> 
> - Install tools you need to build your application
> - Install or update library dependencies
> - Generate your application

[https://docs.docker.com/develop/develop-images/dockerfile\_best-practices/](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)

### References

1. [https://docs.docker.com/develop/develop-images/multistage-build/](https://docs.docker.com/develop/develop-images/multistage-build/)
2. [https://docs.docker.com/develop/develop-images/dockerfile\_best-practices/](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)