Docker Best Practices: Understanding the Differences Between ADD and COPY Instructions in Dockerfiles

COPY vs. ADD tl;dr:

When you search for “Dockerfile best practices,” one of the suggestions you will find is that you always use the COPY instruction instead of the ADD instruction when adding files into your Docker image.

This blog post will explore why this suggestion exists by providing additional detail on the functionality of these two instructions. Once you understand these concepts, you may find scenarios where you can benefit from ignoring the suggestion and using the ADD command instead of COPY.

2400x1260 understanding the differences between add and copy instructions in dockerfiles

Understanding file system build context

Before diving into the differences between ADD and COPY, it’s important to understand the concept of build context. The build context is the set of files and directories that are accessible to the Docker engine when building an image. When you run a docker build command, Docker sends the content of the specified context directory (and its subdirectories) to the Docker daemon. This context forms the scope within which the COPY and ADD instructions operate.

COPY instruction

The COPY instruction is straightforward and does exactly what its name implies: It copies files and directories from a source within the build context to a destination layer in the Docker image. This instruction can be used to copy both files and directories, and all paths on the host are relative to the root of the build context.

Syntax:

COPY <src>... <dest>
  • <src>: The source files or directories on the host.
  • <dest>: The destination path inside the Docker image.

Key points

  • Basic functionality: COPY only supports copying files and directories from the host file system. It does not support URLs or automatic unpacking of compressed files.
  • Security: Because COPY only handles local files, it tends to be more predictable and secure than ADD, reducing the risk of unintentionally introducing files from external sources.
  • Use case: Best used when you need to include files from your local build context into the Docker image without any additional processing.

Example:

COPY ./app /usr/src/app
COPY requirements.txt /usr/src/app/

In this example, the contexts of the local app directory are copied into the /usr/src/app directory inside the Docker image being built. The second command copies the requirements.txt file into the /usr/src/app directory as well.

ADD instruction

The ADD instruction provides the same functionality that the COPY instruction does, but it also has additional functionality that, if misunderstood, can introduce complexity and potential security risks.

Syntax:

ADD <src>... <dest>
  • <src>: The source files (directories or URLs).
  • <dest>: The destination path inside the Docker image.

Key points

  • Extended functionality: In addition to copying local files and directories from the build context, ADD provides the following advanced functionality:
    • Handle URLs: When supplied as a source, the file referenced by a URL will be downloaded to the current Docker image layer at the supplied destination path.
    • Extract archives: When supplied as a source, ADD will automatically unpack and expand archives to the current Docker image layer at the supplied destination path.
  • Flexibility vs. security: Although ADD is more flexible, it does introduce risk. Downloading external URLs into the build process may allow malicious code or contents to be brought into the process. Using ADD with archives may result in unintended consequences if you do not understand how it handles archives.
  • Use case: ADD should only be used when you need specific functionality that it provides and are willing to manage the potential security issues arising from this usage.

Example:

ADD https://example.com/file.tar.gz /usr/src/app/
ADD my-archive.tar.gz /usr/src/app/

In this example, the build process first downloads https://example.com/file.tar.gz and extracts the contents into /usr/src/app in the Docker image layer. In the next step, it takes the local file my-archive.tar.gz and extracts it into the Docker image layer under /usr/src/app.

When to use COPY vs. ADD

  • For most use cases, COPY is the better choice due to its simplicity and security. This instruction allows you to transfer files and directories from your local context into the Docker image you are building.
  • Use ADD only when you need the additional capabilities it offers, but be mindful of potential security implications.

Remote contexts

In addition to traditional file system contexts, Docker also supports remote contexts, which can be particularly useful in cloud environments or for building images from code repositories directly. These include:

  • Git repositories: You can specify a Git repository URL as the build context, allowing Docker to clone the repository and use its content as the context.
docker build https://github.com/username/repository.git#branch
  • Remote URLs: Docker can use remote URLs for the build context. This is useful for building images directly from archives available online.
docker build http://example.com/context.tar.gz
  • OCI images: You can use an OCI image as the build context, which is useful for using pre-built images as the starting point for new builds.
docker build oci://registry.example.com/image:tag

How ADD and COPY behave in remote contexts

Note that both ADD and COPY behave slightly differently when used in a remote context.

Using COPY with remote contexts

COPY still operates within the scope of the build context, and can copy files and directories from the cloned repository into the Docker image. For example, when using a Git repository as the build context, COPY can copy files and directories from the cloned repository into the Docker image. It does not support copying files from URLs or other remote sources directly.

Example with Git repository as build context:

# Using a Git repository as build context
COPY ./src /app/src

In this case, COPY will copy the src directory from the Git repository (the build context) to /app/src in the Docker image.

Example with URL build context:

# Using an archive from a URL
COPY ./src /app/src

In this case, COPY will copy the src directory from the extracted archive (the build context) to /app/src in the Docker image.

Example with OCI image as build context:

# Using an OCI image as build context
COPY /path/in/oci/image /app/path

In this case, COPY will copy the contents from the specified path within the OCI image to the specified destination path in the Docker image.

Using ADD with remote contexts

The ADD instruction can still be used to download files and extract archives as well as copy files from the build context. Note that all the caveats provided about the ADD instruction above apply here as well.

Example with Git repository as build context:

# Using a Git repository as build context
ADD https://example.com/data.tar.gz /data
ADD ./src /app/src

In this example, ADD will download and extract data.tar.gz from the URL into the /data directory in the Docker image. It will also copy the src directory from the Git repository (the build context) to /app/src in the Docker image.

Example with URL build context:

# Using an archive from a URL
ADD https://example.com/data.tar.gz /data
ADD ./src /app/src

In this example, ADD will download and extract data.tar.gz from the URL into the /data directory in the Docker image. It will also copy the src directory from the downloaded/unpacked URL (the build context) to /app/src in the Docker image.

Example with OCI image as build context:

# Using an OCI image as build context
ADD https://example.com/data.tar.gz /data
ADD /path/in/oci/image /app/path

In this scenario, ADD will download and extract data.tar.gz from the URL into the /data directory in the Docker image. It will also copy the contents from the specified path within the OCI image to the specified destination path in the Docker image.

COPY vs. ADD tl;dr:

  • Prefer COPY: For most use cases, COPY is the better choice due to its simplicity and security. Use it to transfer files and directories from your local context or a remote context like a Git repository to the Docker image.
  • Use ADD with caution: Opt for ADD only when you need its additional functionalities, like downloading files from URLs or automatically extracting archives (Figure 1). Always be mindful of the potential security implications when using ADD.
Diagram showing concepts that are also explained in the blog post.

Conclusion

Understanding the differences between ADD and COPY instructions in Dockerfiles and how they can be affected by build context can help you build more efficient and secure Docker images. Although COPY offers a straightforward way to include local files, ADD provides additional flexibility with the cost of increased complexity and potential security risks.

Learn more