Understanding the Docker Build Context (Why You Should Use Dockerignore)
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:latestCOPY 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.
ncG1vNJzZmivp6x7qbvWraagnZWge6S7zGibnq6fpcBwwc2dnKurpJa7pbXNoGStoJVisbCvyp6pZpqlnrmlecKopa2dqKl6uLTYZrCorV2otbDBy51krquVYrGwr8qeqaKfnqS%2Fpns%3D