3 min read

The difference between the COPY and ADD commands in a Dockerfile

The difference between the COPY and ADD commands in a Dockerfile

Transferring files from host to container image is typical while creating container images, thus we have two options for moving files or directories to the container. However, while both options may provide the same outcome, the ADD command has a different use.

If you haven't already, I strongly suggest you read the previous post about Docker build and push:

How to Build and Push a Container Image
In this article, I will discuss how to create container images with Docker and Podman. It is important to note that both follow to the OCI Spec

COPY instruction

I'll use examples to demonstrate how the COPY command works. We often utilize copy for source code, assets, settings, and so forth...

The following example uses COPY in three ways:

FROM alpine:3.17 as base


FROM ubuntu:22.10 as super_secret

RUN echo "you_should_not_use_secrets_here" > /secret.txt

FROM base as app

COPY --from=super_secret /secret.txt /app/secret.txt

COPY ./assets/hello.txt /app/hello.txt

COPY ./tools /app

RUN chmod +x /app/info.sh

ENTRYPOINT ["/app/info.sh"]
  • COPY --from=super_secret /secret.txt /app/secret.txt, copy file from a stage called super_secret;
  • COPY ./assets/hello.txt /app/hello.txt, copy the file from the host to the container;
  • COPY ./tools /app, copy the directory from the host to the container;
That covers our discussion of the most common COPY applications.

ADD instruction

Although the ADD command behaves similarly to the COPY command in that it copies files, ADD has been developed to copy from local (host) or remote files (http) to the container image; if this file has been compressed (tar.gz), the content will be extracted and copied to the destination directory.

The following is an example of a Dockerfile that copies a compressed file and extracts it to the destination directory:

FROM alpine:3.17


ADD ./assets/tools.tar.gz /app

RUN chmod +x /app/assets.sh

ENTRYPOINT ["/app/assets.sh"]

Following that, we have a Dockerfile containing a remote file that will be extracted to the target directory:

FROM alpine:3.17


ADD https://github.com/williampsena/docker-recipes/raw/main/build/samples/assets/tools.tar.gz /app

RUN tar -xf tools.tar.gz

RUN chmod +x /app/assets.sh

ENTRYPOINT ["/app/assets.sh"]

Two builds will generate the identical files, but the first will use the host path and the second will use the remote path. The file tools.tar.gz will be present in the container using ADD remote example, which you should delete using the RUN command to save up space.


COPY and ADD supports extra features such as chmod and chown to modify the permissions of transferred files, however only the Linux container supports these features.

COPY --chown foo:foo --chmod 644 ./assets /assets
ADD --chown foo:foo --chmod 644 ./assets/foo.tar.gz /assets


  1. As previously stated, ADD and COPY achieve the same result when transferring files, although Docker advises using COPY rather than ADD.
  2. Because image size matters, Docker recommends avoiding using ADD to download packages from remote URLs; instead, use curl or wget. That way, when the files have been extracted, you may remove them and avoid having to add another layer to your image. You should, for example, avoid doing the following:
# curl
RUN mkdir -p /tmp/assets && \ 
curl -SL https://github.com/williampsena/docker-recipes/raw/main/build/samples/assets/tools.tar.gz | tar xvfz - -C /tmp/assets

# wget
RUN mkdir -p /tmp/assets && \ 
wget -qO- https://github.com/williampsena/docker-recipes/raw/main/build/samples/assets/tools.tar.gz | tar xvfz - -C /tmp/assets

Final thoughts

⚠️ The Dockerfiles recipes and assets might be found on my Github repository.

With practical build samples, we learned the difference between COPY and AND today.  I'll go through additional Docker instructions in future blogs.

Keep your 🧠 kernel up to date. God bless 🙏🏿 you.