---
title: "Utilizing Docker BuildKit to Optimize Image Builds"
description: "Discover how to use Docker BuildKit to optimize your Docker images and cut down on the space necessary in this ATA Learning tutorial!"
canonical: "https://adamtheautomator.com/docker-buildkit/"
---

# Utilizing Docker BuildKit to Optimize Image Builds

> Discover how to use Docker BuildKit to optimize your Docker images and cut down on the space necessary in this ATA Learning tutorial!

Source: https://adamtheautomator.com/docker-buildkit/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![How to Optimize Image Builds with Docker BuildKit](https://adamtheautomator.com/wp-content/uploads/2023/05/docker-buildkit.jpg)

# How to Optimize Image Builds with Docker BuildKit

[![](https://secure.gravatar.com/avatar/572a248f516b6d0cd566cb44fdbd9336f30ef95aa0f6d78f118c1f47b1b6d6b7?s=192&d=mm&r=g)Arvid Larson](https://adamtheautomator.com/author/arvid-larson/)23 May 20237 min. read

Categories: [DevOps](/category/devops/)

Tags:[Docker](/tag/docker/)

Table of Contents

*   [Prerequisites](#prerequisites)
*   [Building a Docker Image the Traditional Way with Docker BuildKit](#building-a-docker-image-the-traditional-way-with-docker-buildkit)
*   [Building a Docker Image with Multi-Stage Build](#building-a-docker-image-with-multi-stage-build)
*   [Building a Docker Image for a Specific Build Stage](#building-a-docker-image-for-a-specific-build-stage)
*   [Speeding Up the Build Process with Docker BuildKit Mount Cache](#speeding-up-the-build-process-with-docker-buildkit-mount-cache)
*   [Conclusion](#conclusion)

In the new era of containerization, building images for applications must be fast and effective. But how exactly? With Docker BuildKit, you can build images in parallel while reducing unnecessary dependencies.

This tutorial will walk you through the process of utilizing BuildKit to optimize Docker image build and save time for other essential tasks.

Read on and get started enjoying building images with BuildKit!

## Prerequisites

This tutorial comprises hands-on demonstrations. To follow along, ensure that you have the following:

*   Docker Engine – This tutorial uses [Docker v23 installed](https://docs.docker.com/engine/install/ubuntu/) on [Ubuntu](https://adamtheautomator.com/docker-ubuntu/) 22.04.

Related:[How to Install and Use Docker on Ubuntu (In the Real World)](https://adamtheautomator.com/docker-ubuntu/)

*   [Git installed](https://phoenixnap.com/kb/how-to-install-git-on-ubuntu).

## Building a Docker Image the Traditional Way with Docker BuildKit

Before getting started with BuildKit to optimize a Docker image build, you must download a [sample Node.js project](https://github.com/Adam-the-Automator/hello-node-docker.git) you will use for this tutorial. You will later build a Docker image using a traditional/old-way Dockerfile configuration to ensure the application works.

Related:[How to Create an HTTPS NodeJS Web Service with Express](https://adamtheautomator.com/https-nodejs/)

To build a Docker image the traditional way:

1\. Run the following `git` command to `clone` the sample Node.js project (`hello-node`).

```bash
git clone <https://github.com/Adam-the-Automator/hello-node-docker.git> hello-node
```

![docker buildkit - Downloading a sample Node.js project (hello-node)](https://adamtheautomator.com/wp-content/uploads/2023/05/image-231.png)

Downloading a sample Node.js project (hello-node)

2\. Next, run the command below to list (`ls`) the files included in the `hello-node` project.

```docker
ls hello-node/
```

Below is the list of files that are available in the project:

*   _**app.js**_ – A simple “hello world” Node.js application with Express.js.
*   _**package.json**_ – Contains information about the application and its dependencies.
*   _**Dockerfile.traditional** –_ A _Dockerfile_ for the containerized Node.js application with a traditional approach.
*   _**Dockerfile.multi** –_ A _Dockerfile_ for the Node.js application with a multi-stage build.
*   _**Dockerfile.mount** –_ A _Dockerfile_ for the Node.js application with multi-stage build and BuildKit mount cache implementation.

![Listing all files included in the hello-node project](https://adamtheautomator.com/wp-content/uploads/2023/05/image-230.png)

Listing all files included in the hello-node project

3\. With the project cloned, open the _Dockerfile.traditional_ file with your preferred editor, and ensure you have the following configuration.

Related:[Essential Tips for Installing & Using Sublime Text on Ubuntu](https://adamtheautomator.com/sublime-text-on-ubuntu/)

```docker
# syntax=docker/dockerfile:1

# Use node 18 as the base image from Docker Hub
FROM node:18

# Create a working directory /usr/src/app
WORKDIR /usr/src/app

# Copy the source code to the container
COPY . .

# Install app dependencies via Yarn
RUN yarn install

# Expose port 8080 for app.js
EXPOSE 8080

# Define a default command to start app.js
CMD [ "node", "app.js" ]
```

4\. Now, run the below [`docker build`](https://docs.docker.com/engine/reference/commandline/build/%3E) command to build the new Docker image named `app_traditional` (arbitrary) against the `Dockerfile.traditional` file.

```bash
docker build -t app_traditional -f Dockerfile.traditional .
```

![Building a Docker Image via the traditional way](https://adamtheautomator.com/wp-content/uploads/2023/05/image-229.png)

Building a Docker Image via the traditional way

5\. Now, execute the command below to list all available Docker [`images`](https://docs.docker.com/engine/reference/commandline/images/).

```bash
docker images
```

Related:[How to Update Docker Images to the Latest Version](https://adamtheautomator.com/update-docker/)

The output below shows the **app\_traditional** image is created with a total size of **1GB**.

![Checking the list of all Docker images](https://adamtheautomator.com/wp-content/uploads/2023/05/image-228.png)

Checking the list of all Docker images

6\. Execute the following command to `run` a new container called `app1` (arbitrary) in the background (`-d`) with the Docker image `app_traditional`.

This command exposes port `8080` on both the Docker host and the container in the format of `container:host`.

```bash
docker run -d --name app1 -p 8080:8080 -t app_traditional
```

![Running a new container](https://adamtheautomator.com/wp-content/uploads/2023/05/image-227.png)

Running a new container

7\. Run the following command to list all running containers.

```bash
docker ps
```

As you can see from the output, the **app1** container runs on port **8080** on the Docker host.

![Checking the list of running container](https://adamtheautomator.com/wp-content/uploads/2023/05/image-226.png)

Checking the list of running container

8\. Now, execute the `curl` command below to access the container via `localhost`.

```bash
curl http://localhost:8080/
```

Related:[CURL Linux Command : Learning By Example](https://adamtheautomator.com/curl-linux/)

You will see the output below if the process is successful.

![Accessing container app1 via curl ](https://adamtheautomator.com/wp-content/uploads/2023/05/image-225.png)

Accessing container app1 via curl

Alternatively, open your web browser, and visit the server IP access followed by port 8080 (i.e., _192.168.5.20:8080_) to access the container.

![Accessing container app1 via a web browser](https://adamtheautomator.com/wp-content/uploads/2023/05/image-224.png)

Accessing container app1 via a web browser

9\. Lastly, run the following [`docker rm`](https://docs.docker.com/engine/reference/commandline/rm/) command to forcibly (`--force`) delete the running container `app1` and clean up your environment.

This command does not provide output, but deleting the container is a necessary preparation as you will later use port 8080 to run a new version of the container.

```bash
docker rm app1 --force
```

## Building a Docker Image with Multi-Stage Build

One of the essential features in the BuildKit is a multi-stage. This feature allows you to use multiple `FROM` parameters on your Dockerfile and name each stage via `AS` parameter. Moreover, you can use different Docker images in multi-stage builds.

To build a Docker image with the multi-stage build, you will use _Dockerfile.multi_ file as follows:

1\. Open the file _Dockerfile.multi_, and you will see two stage builds where:

Stage 1 – Named build-env with Node.js 18 (node:18) as the base image. This stage copies (COPY) the _package.json_ file, install application dependencies and COPY the _app.js_ file.

Related:[How to Copy Files with Docker cp to your Docker Container](https://adamtheautomator.com/docker-cp/)

Stage 2 – The final build of the application with base image [Distroless](https://github.com/GoogleContainerTools/distroless). This stage copies the build from build-env to the _/app_ working directory and exposes port 8080 for the application.

```docker
# syntax=docker/dockerfile:1

# Stage 1 - Using node 18 as the base image for the build-env
FROM node:18 AS build-env

# Create a working directory /app
WORKDIR /app

# Copy package.json and install dependencies
COPY package.json package.json
RUN yarn install

# Copy the app.js to /app directory
COPY app.js /app

# Stage 2 - Use the distroless Docker image for the final build
FROM gcr.io/distroless/nodejs

# Setup working directory /app
WORKDIR /app

# Copy the application with its dependencies into a distroless image
COPY --from=build-env /app /app

# Expose port 8080 for the app.js
EXPOSE 8080

# Start the app.js
CMD ["app.js"]
```

2\. Next, run the following command to `build` the new Docker image called `app_multi_stage` (arbitrary) in the current directory (`.`) via the `Dockerfile.multi` file.

```bash
docker build -t app_multi_stage -f Dockerfile.multi .
```

![Building a Docker image with multi-stage](https://adamtheautomator.com/wp-content/uploads/2023/05/image-233.png)

Building a Docker image with multi-stage

3\. Once built, run the command below to verify the list of available Docker `images`.

```bash
docker images
```

As you can see, the output below shows the newly-built image (`app_multi_stage`) with a size of **166MB**, which is smaller than the `app_traditional` image.

![Listing all Docker images](https://adamtheautomator.com/wp-content/uploads/2023/05/image-234.png)

Listing all Docker images

4\. Now, execute the below commands to `run` a new container (`app2`) with the `app_multi_stage` image and verify the running container list.

```bash
docker run -d --name app2 -p 8080:8080 -t app_multi_stage
docker ps
```

The output below shows confirms the **app2** container is running on port **8080**.

![Running a new container](https://adamtheautomator.com/wp-content/uploads/2023/05/image-235.png)

Running a new container

5\. With the new container running, execute the `curl` command below to access the container (`app2`) from your terminal.

```bash
curl http://localhost:8080/
```

![Accessing container app1 via curl](https://adamtheautomator.com/wp-content/uploads/2023/05/image-237.png)

Accessing container app1 via curl

Or, navigate to the Docker host IP address followed by port 8080 (i.e., _http://192.168.5.50:8080_).

![Accessing container app1 via a web browser](https://adamtheautomator.com/wp-content/uploads/2023/05/image-224.png)

Accessing container app1 via a web browser

6\. Next, run the below command to delete (`rm`) the `app2` container.

```bash
docker rm app2 --force
```

7\. Lastly, run the below command to rebuild the `app_multi_stage` image.

BuildKit creates caches for each build that you have done previously. As a result, when you change the source code, rebuilding the Docker image becomes faster.

```bash
time docker build -t app_multi_stage -f Dockerfile.multi .
```

As seen in the following output, rebuilding the Docker image only took four (**4**) seconds.

![Rebuilding a Docker image via build caches](https://adamtheautomator.com/wp-content/uploads/2023/05/image-232.png)

Rebuilding a Docker image via build caches

## Building a Docker Image for a Specific Build Stage

Besides a multi-stage build, BuildKit lets you create Docker images from specific build stages. This feature can help debug each build stage and set up tests with additional tools.

To build a Docker image for a specific build stage in a Dockerfile:

1\. Open the `package.json`, where you will see the below configuration, which installs one dependency, Express v4.18.2 (`"express": "^4.18.2"`).

```json
{
  "name": "hello_node",
  "version": "0.1",
  "main": "app.js",
  "dependencies": {
    "express": "^4.18.2"
  }
}
```

2\. Next, run the following command to `build` a new Docker image (`build-env1.0`) for the `build-env` stage in the `Dockerfile.multi` file.

```bash
docker build --target build-env -t build-env1.0 -f Dockerfile.multi .
```

![Stopping at specific build Docker multi-stage build](https://adamtheautomator.com/wp-content/uploads/2023/05/image-243.png)

Stopping at specific build Docker multi-stage build

3\. Now, run the command below to list all Docker `images`.

```bash
docker images
```

The output below confirms the **build-env1.0** image exists with a size of **1GB**.

![Verifying the and the build-env1.0 image exists](https://adamtheautomator.com/wp-content/uploads/2023/05/image-242.png)

Verifying the and the build-env1.0 image exists

4\. With the Docker image created, execute the following command to `run` a new container with the `build-env1.0` Docker image.

```bash
docker run -it build-env1.0 /bin/sh
```

![Running a new container with the build-env1.0 Docker image](https://adamtheautomator.com/wp-content/uploads/2023/05/image-241.png)

Running a new container with the build-env1.0 Docker image

5\. Once logged in to the container, run the following command to verify the Express.js (`express`) package.

```bash
yarn list --pattern express
```

The following results show that **Express.js v4.18.2** is installed on the `build-env1.0` image.

![Checking dependencies at the build-env stage](https://adamtheautomator.com/wp-content/uploads/2023/05/image-240.png)

Checking dependencies at the build-env stage

6\. Lastly, run the exit command to `exit` out of the container.

```docker
exit
```

![Exiting out of the container](https://adamtheautomator.com/wp-content/uploads/2023/05/image-239.png)

Exiting out of the container

## Speeding Up the Build Process with Docker BuildKit Mount Cache

Another outstanding feature from BuildKit is speeding up building an image by appending the `--mount=type=cache` flag within the `RUN` parameter. This flag allows you to cache the target directory to be preserved between builds. Moreover, most package managers like yarn, npm, pip, and apt can use this flag.

To see how to BuildKit speeds up the build process:

1\. Open the file _Dockerfile.mount_ to see the following configurations.

The flag `--mount=type` within the `RUN` parameter caches the Node.js packages on the target cache directory `/root/.yarn`.

```docker
# syntax=docker/dockerfile:1

# Stage 1 - Use the node 18 image as the builder and install dependencies
FROM node:18 AS build-env

# Create a working directory
WORKDIR /app

# Copy package.json
COPY package.json package.json

# Installing dependencies via Yarn
# Using the parameter to enable cache of the target directory /root/.yarn
# to be preserved between builds
RUN --mount=type=cache,target=/root/.yarn,sharing=shared \\
ls -l --time-style=+"%b %d %Y %H:%M" /root/.yarn \\
&& YARN_CACHE_FOLDER=/root/.yarn yarn install \\
&& ls -lt --time-style=+"%b %d %Y %H:%M" /root/.yarn/v6

# Copy app.js
COPY app.js /app

# Stage 2 - final image using Distroless
FROM gcr.io/distroless/nodejs

# Create a working directory
WORKDIR /app

# Copy the /app from build-env stage 1
COPY --from=build-env /app /app

# Expose port 8080 for app.js
EXPOSE 8080

# Start command
CMD ["app.js"]
```

2\. Next, run the following command to `build` a new Docker image (`test_cache`) against the `Dockerfile.mount` file.

```docker
docker build -t test_cache --progress=plain -f Dockerfile.mount .
```

The results below show the time (**10:26**) the Node.js packages are installed (not the duration).

![Building a Docker image against the Dockerfile.mount file](https://adamtheautomator.com/wp-content/uploads/2023/05/image-246.png)

Building a Docker image against the _Dockerfile.mount_ file

3\. Once you have created the image, open the _package.json_ file and add the new package `express-validator`, as shown below, and save the changes.

```json
  "dependencies": {
    "express": "^4.18.2",
    "express-validator": "^7.0.0"
  }
```

4\. Now, run the following command to rebuild the `test_cache` Docker image to ensure that Node.js packages are cached between builds.

```json
docker build -t test_cache --progress=plain -f Dockerfile.mount .
```

Look at when the new package `express-validator` was installed at **10:28** from the internet, while other packages were installed from the cache at **10:26**.

![Rebuilding a Docker image while Node.js packages are cached between builds](https://adamtheautomator.com/wp-content/uploads/2023/05/image-245.png)

Rebuilding a Docker image while Node.js packages are cached between builds

## Conclusion

Docker is already remarkable on its own as a solution for containerizing applications. But in this tutorial, you have learned that together with Docker BuildKit, you can optimize Docker image build in many ways.

With multi-stage builds for testing and debugging dependencies, having the Docker BuildKit at your disposal is proven imperative. In addition, with the BuildKit mount cache, you can now speed up your Docker image build process, saving you time so you can focus on other crucial tasks.

Now, why not explore more about BuildKit? Perhaps try building Docker images for [multiple system architectures](https://docs.docker.com/build/building/multi-platform/)?

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fdocker-buildkit%2F&text=How%20to%20Optimize%20Image%20Builds%20with%20Docker%20BuildKit)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fdocker-buildkit%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fdocker-buildkit%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/02/featured_image-13.webp)

### [Azure Container Apps: Instant Preview Environments per PR](/build-ephemeral-preview-environments-every-pr/)

Build isolated preview environments for every pull request using Azure Container Apps revision labels and Azure DevOps pipelines with scale-to-zero economics.

![](https://adamtheautomator.com/wp-content/uploads/2025/11/55418e927e511ae263219c072e27d637c2a967a5036de7020b329582db775c26.png)

### [Automating Docker Container Health Checks with Python and Local Notifications](/docker-health-checks-python/)

Docker's built-in health checks are passive—they tell Docker when a container fails, but do they tell you? In this tutorial, we'll build a lightweight Python monitoring system that runs entirely on your infrastructure with zero external dependencies. You'll learn to detect container failures in real-time, send instant alerts, and maintain a complete audit log of every state change.

![](https://adamtheautomator.com/wp-content/uploads/2023/07/gitea-docker.jpg)

### [Gitea Docker: Your Ultimate Self-Hosted Git Solution](/gitea-docker/)

Learn how to install a Gitea Docker instance and self-host Git repositories securely in this ATA Learning tutorial!

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
