Quick Links

The Docker build context refers to the files and directories that will be available to the Docker engine when you run docker build. Anything not included in the build context won't be accessible to commands in your Dockerfile.

You should audit your use of docker build to keep your build contexts small. Accidentally including unnecessary files can result in an excessively large build context, which will lead to longer builds.

What Is the Build Context?

Here's a simple docker build command:

        docker build . -t my-image:latest
    

This builds a Docker image using the Dockerfile found in your working directory. The resulting image will be tagged as my-image:latest, although this detail isn't important to this tutorial.

Within your Dockerfile, you'll likely use COPY to add files and folders into your image:

        FROM httpd:latest

COPY index.html /usr/local/apache2/htdocs/index.html
COPY css/ /usr/local/apache2/htdocs/css/

This example copies the index.html file and css directory into your container. At first glance, it looks like the COPY statement simply references a path that's resolved relative to your working directory.

This isn't quite the case. COPY can only access resources available in the build context. In this example, the build context is the working directory, so the files and folders within it are available. By default, Docker uses the contents of the directory passed to docker build as the build context.

Why Is the Build Context Used?

The build context is important because the Docker CLI and Docker Engine might not be running on the same machine. When you run docker build, the CLI sends the files and folders to build to the Docker engine. This set of files and folders becomes the build context.

Furthermore, not every build context is as straightforward as reusing your working directory. Docker also supports Git repository URLs as the path given to docker build. In this case, the build context becomes the content of the specified repository.

The build context's default "include all" behavior is fine for many small repositories. Problems become apparent once you add files to your working directory that aren't used by your Dockerfile. Resources such as prebuilt binaries, documentation files, and dependency libraries will be included in the build context even though they're redundant.

Including too many assets in the build context can become a performance drain. You're needlessly copying files that will never be used. The slowdown will be particularly evident if you're connected to a remote Docker daemon or if you're using a slow mechanical hard drive. You'll see "sending build context to Docker daemon" in your shell while the copy is completed.

Docker does try to minimize redundant copying on its own. The BuildKit build backend---used since Docker 18.09---added support for incremental transfers. This means that Docker will usually only need to copy files added or changed since your last build. It'll still copy the whole lot on the first build.

Excluding Resources from the Build Context

To resolve wasteful copying for good, you must tell Docker what it can omit from the build context. Let's start by creating a Dockerfile:

        FROM node:latest
WORKDIR /my-app
COPY package.json package.json
COPY package-lock.json package-lock.json
COPY src/ .
RUN npm install

This simple Dockerfile could be used by an application written in Node.js. Node.js programs use npm as their package manager. Packages are installed to a node_modules folder. When you run npm install locally, during development, the packages will be downloaded to the node_modules folder in your working directory.

The Dockerfile runs npm install itself to acquire the dependencies. This ensures that the image is fully self-contained. There's no need to copy in the local node_modules folder, as it's not used by the Dockerfile.

Despite this, Docker will still include the node_modules folder in the default build context. To exclude it, create a .dockerignore file in your working directory. This file has a similar syntax to .gitignore.

        node_modules/
    

Any paths listed in .dockerignore will be excluded from the build context. You should make sure that .dockerignore is kept updated with changes to your project's filesystem structure. You can substantially reduce Docker build context copying time by checking that only relevant paths (those actually used by your Dockerfile) are present in the build context.

In the case of our example, the node_modules folder could include thousands of files if we have a lot of dependencies in our project. Copying them to the Docker daemon as part of the build context could take several seconds and would be a wasteful operation. The Dockerfile completely ignores them, fetching its own dependencies via npm install instead.

Other Build Context Issues

Not using .dockerignore can introduce other issues, too. A Dockerfile with this line is particularly problematic:

        COPY . /my-app
    

This will copy everything in your working directory. This might seem like a good idea until you realize that your .git history and any secret files will also end up within your container.

Copying an unfiltered build context also prevents Docker layer caching from working effectively. As something in your working directory will probably change between builds, Docker would need to run the COPY instruction every time. This would create a new layer---and new layers for any subsequent instructions---even if the assets you're interested in haven't changed.

Compressing the Build Context

You can compress the build context to further improve build performance. Pass the --compress flag to docker build to apply gzip compression. The compression occurs before the context is sent to the Docker daemon.

        docker build . -t my-image:latest --compress
    

This can improve performance in some scenarios. The compression adds its own overheads, though---your system now needs to compress the context, and the receiving Docker daemon has to uncompress it. Using compression could actually be slower than copying the original files, in some circumstances. Experiment with each of your images to assess whether you see an improvement.

Conclusion

The Docker build context defines the files that will be available for copying in your Dockerfile. The build context is copied over to the Docker daemon before the build begins.

Build contexts default to including the contents of the directory or Git repository you passed to docker build. You can omit items from the build context by creating a .dockerignore file. This increases efficiency by reducing the amount of redundant data passed to the Docker daemon.