Home About Me

When to Use COPY and ADD in a Dockerfile

COPY: copying files into an image

The COPY instruction has two forms:

COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]

Like RUN, it can be written either in a shell-like form or in a JSON array form.

COPY takes files or directories from the build context and places them into the image at the specified target path, creating a new image layer in the process. A common example is:

COPY package.json /usr/src/app/

The source path can include more than one item, and it may also use wildcards. These wildcards follow Go’s filepath.Match rules, for example:

COPY hom* /mydir/
COPY hom?.txt /mydir/

The target path can be an absolute path inside the container, or a relative path based on the current working directory. The working directory can be set with WORKDIR. The target directory does not need to exist in advance; if the required directory structure is missing, Docker creates it before copying the files.

One detail that is easy to overlook is that COPY preserves file metadata. Permissions such as read, write, and execute bits are retained, as are file modification times. This is especially useful when customizing images and when build-related files are managed through Git.

ADD: similar to COPY, but with extra behavior

ADD uses essentially the same syntax and basic copying behavior as COPY, but it includes a few additional features.

One of those features is that the source path may be a URL. In that case, the Docker engine attempts to download the file from the URL and place it at the target path.

However, this is rarely the best approach. A file downloaded this way is assigned permissions of 600 automatically. If different permissions are needed, an additional RUN layer is required to change them. If the downloaded file is an archive that needs to be unpacked, another RUN instruction is also needed for extraction. In practice, it is usually cleaner to use RUN together with tools such as wget or curl, then handle permissions, extraction, and cleanup in a controlled way. For that reason, using ADD for URL downloads is generally not recommended.

The more useful special behavior of ADD appears when the source path is a tar archive. If the archive is compressed with gzip, bzip2, or xz, Docker automatically extracts it into the target path.

This automatic extraction can be exactly what is needed in some Dockerfiles. For example, an official ubuntu image may use a pattern like this:

FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
...

In cases like this, ADD is convenient because the archive is intended to become the filesystem content of the image. But if the goal is to copy the compressed file itself into the image without unpacking it, ADD is the wrong instruction; COPY should be used instead.

Docker’s official Dockerfile best practices recommend using COPY whenever possible. The reason is simple: COPY has clear semantics—it copies files. ADD does more, and that extra behavior can make the Dockerfile less obvious to readers and sometimes less predictable.

There is also a build-performance consideration: ADD can invalidate the image build cache, which may make subsequent builds slower.

A practical rule is therefore straightforward: use COPY for ordinary file copying, and reserve ADD only for the cases where its automatic archive extraction is actually needed.