Improving Docker Container Performance with Multi-Stage Builds

Enes Turan
5 min readDec 8, 2022

--

Docker allows developers to create and manage lightweight, portable, and self-sufficient containers for their applications. One of the key features of Docker is the ability to use multi-stage builds to create Docker images in multiple stages. This allows developers to create more efficient and optimized Docker images for their applications.

In a multi-stage Docker build, a Dockerfile is split into multiple stages, each of which produces a temporary image that is used as the starting point for the next stage. This allows developers to include multiple base images in their build and to keep unnecessary files and dependencies out of the final image.

Multi-stage builds are a great way to create Docker images that are optimized for their intended use. You can selectively copy only the necessary files and dependencies from one stage to another, leaving behind everything you don’t want in the final image.

For example, let’s say you have a Go application that you want to deploy as a Docker image. In the first stage of the build, you could use a base Go image to build the application and run any necessary tests. In the second stage, you could use a smaller, production-ready image (such as Alpine Linux) as the base and copy only the built application code and its dependencies from the first stage. This would result in a final image that is much smaller and more efficient than if you had used the base Go image for the final image.

With multi-stage builds, you can build your Docker images in multiple stages, each of which produces a temporary image that is used as the starting point for the next stage. This allows you to create more efficient and optimized images by selectively copying only the necessary files and dependencies from one stage to another.

Here is an example of a Dockerfile that uses a multi-stage build to create a Docker image for a Go application:

# Stage 1: Build the application
FROM golang:1.19 as builder

COPY . /app
WORKDIR /app

RUN go build -o myapp

# Stage 2: Create the final image
FROM alpine:3.17.0

COPY --from=builder /app/myapp /main

CMD ["/main"]

In this example, the first stage uses the golang:1.19 image to build the application, while the second stage uses the alpine:3.17.0 image to create the final Docker image. The COPY command is used to copy the built Go binary from the first stage to the second stage, resulting in a final image that contains only the necessary files and dependencies.

Multi-stage builds allow you to use multiple base images in your Docker builds and to selectively copy the artifacts you need from each stage into the final image, resulting in smaller and more efficient images.

Another example of a multi-stage build is for a Node.js application. The Dockerfile for this build would look something like this:

# Stage 1: Build the application
FROM node:19 as builder

COPY . /app
WORKDIR /app

RUN npm install && npm run build

# Stage 2: Create the final image
FROM node:19-alpine

COPY --from=builder /app/dist /app/dist

CMD ["node", "/app/dist/main.js"]

In this Dockerfile, the first stage uses the node:19 image to build the Node.js application and run any tests, while the second stage uses the node:19-alpine image to create the final Docker image. In the example Dockerfile provided, the RUN instruction in the first stage runs the npm install and npm run build commands. The npm install command installs the necessary dependencies for the Node.js application, as specified in the package.json file. The npm run build command then runs any build scripts that are specified in the package.json file, which could include tasks such as transpiling the application code from a higher-level language (e.g. TypeScript) to JavaScript.

The COPY instruction in the second stage then copies the built application code from the /app/dist directory of the first stage to the /app/dist directory of the second stage resulting in a final image that contains only the necessary files and dependencies .The CMD instruction in the second stage then specifies the command that will be run when a container is created from the final Docker image, which is to run the main.js file from the /app/dist directory.

In addition to using multi-stage builds to create more efficient Docker images, Docker also uses a layered filesystem to manage the images and their dependencies. Each Docker image is composed of one or more layers, where each layer represents a set of changes to the filesystem. When you build a Docker image, each instruction in the Dockerfile creates a new layer in the image. These layers are stacked on top of each other, and the final Docker image is the combination of all the layers. This allows for efficient storage and transfer of images, since only the layers that have changed need to be transferred when an image is updated. It also allows for efficient sharing of images, since multiple images can share common layers, reducing the overall storage and network bandwidth requirements.

For example, when you run the COPY or ADD command in a Dockerfile, a new layer is created that contains the copied files. When you run the RUN command, a new layer is created with the results of the command. These layers are stacked on top of each other, and the final Docker image is the combination of all the layers.

The use of layers in Docker allows for efficient storage and transfer of images, since only the layers that have changed need to be transferred when an image is updated. It also allows for efficient sharing of images, since multiple images can share common layers, reducing the overall storage and network bandwidth requirements.

You can read this article to get more information about Docker layers:

In conclusion, multi-stage Docker builds and the layered filesystem are key features of Docker that allow developers to create efficient and optimized images for their applications. By using multi-stage builds, developers can create images that include multiple base images and keep unnecessary files and dependencies out of the final image.

--

--

No responses yet