When building Docker images, the Dockerfile serves as a blueprint for constructing the image. It contains a set of instructions that Docker follows to build the image. Among these instructions, the COPY and ADD commands are often used to include files from the local file system into the Docker image. Although they seem similar, there are distinct differences in their functionality and use cases.
What is a Dockerfile?
Before diving into the specifics of COPY and ADD, it’s important to understand what a Dockerfile is. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.
The COPY Command
The COPY command in a Dockerfile copies new files or directories from a source and adds them to the filesystem of the container at the specified destination. It is straightforward and explicitly designed to copy local files.
Syntax:
COPY [--chown=<user>:<group>] <source>... <destination>Example:
COPY ./app /usr/src/appThis line in a Dockerfile would copy the local directory ./app into the /usr/src/app directory inside the Docker container.
Key Points for COPY:
-
Only local files or directories can be copied.
-
Does not support downloading URLs.
-
Does not have the capability to automatically extract tar files.
The ADD Command
The ADD command is more complex and powerful. It copies files and directories like COPY, but it also has the capability to handle URL sources and automatically extract compressed files.
Syntax:
ADD [--chown=<user>:<group>] <source>... <destination>Example:
ADD https://example.com/example.tar.gz /var/www/htmlThis command would download the example.tar.gz file from the specified URL and extract it into /var/www/html inside the container.
Key Points for ADD:
-
Can copy new files or directories like
COPY. -
Can handle URL sources to download files directly into the image.
-
Automatically extracts archives (such as tarballs).
When to Use COPY vs. ADD
-
Use
COPY: When you need to simply copy files from your local file system into the Docker image without needing any additional capabilities,COPYis preferred. It is clearer and more explicit, making the Dockerfile easier to understand and maintain. -
Use
ADD: When you need the additional capabilities of handling URLs and extracting compressed files,ADDis the appropriate choice. However, it should be used cautiously because its behavior can introduce complexity and unpredictability in the build process.
Best Practices
-
Prefer
COPYoverADD: Unless you need the additional functionality thatADDprovides, usingCOPYis preferable. This is becauseCOPYis simpler and less prone to errors. -
Explicit is better than implicit: In Dockerfiles, clarity and explicit behavior are crucial for maintainability and reliability. Using
COPYmakes it clear that files are being copied without any additional processing. -
Security considerations: When using
ADDto download files from URLs, ensure the source is secure and reliable to avoid security issues.
Conclusion
Understanding the differences between COPY and ADD in Dockerfiles helps in making better decisions when writing Dockerfiles. While both commands have their place, choosing the right command based on the requirement can simplify Docker image creation and make your Dockerfiles more efficient and secure.
Related Reading
- Understanding CMD and ENTRYPOINT in Dockerfiles
- Access Docker socket via TCP
- Docker Compose: Orchestrating Multi-Container Applications
- Copying Files Between Docker Containers and Host Machines
- WordPress, Docker, NGINX, and MySQL via Ansible
Gotchas That’ll Bite You at 2 AM
Knowing the rules is one thing. Getting burned by the edge cases is how you actually internalize them.
The silent tarball extraction trap. Say you’re packaging your app and you genuinely want to ship a .tar.gz as a file — not extracted, just sitting there for something downstream to deal with. Use ADD and it’ll silently unpack it. Your container runs, the file isn’t where you expected, and you spend 20 minutes docker exec-ing around wondering what happened. COPY respects the archive as a file. ADD does not.
Cache invalidation with ADD + URLs. Docker’s layer cache is smart about local files — it hashes them and only rebuilds if content changes. URLs? It can’t do that. Every time Docker processes ADD https://example.com/something.tar.gz /dest, it has to assume the remote content might have changed. Depending on your Docker version and build flags, you may get stale content or forced rebuilds at the worst possible moment. The more predictable move is RUN curl or RUN wget with an explicit version pinned in the URL.
# This looks clean but you'll fight cache invalidation foreverADD https://releases.example.com/tool-latest.tar.gz /usr/local/
# This is more verbose but you control exactly what you getRUN curl -fsSL https://releases.example.com/tool-1.4.2.tar.gz \ | tar -xz -C /usr/local/ \ && rm -rf /tmp/build--chown doesn’t apply to URL sources. If you’re using ADD with a URL and you think --chown=app:app is setting ownership on the downloaded file — it’s not. The flag works fine for local files and archives, but URL sources ignore it. You’ll need a RUN chown after the fact, which is an extra layer anyway. At that point, just use RUN curl and be done with it.
Permissions on extracted archives. When ADD unpacks a tarball, it preserves whatever permissions were baked into the archive. That might be root:root and 755, or it might be some nonsense from whoever created the archive. Don’t assume. Always verify with a quick docker run --rm <image> ls -la /your/path after a build if you’re relying on specific permissions.
The pattern that avoids most of these headaches: use COPY for everything you own and control, reach for RUN curl | tar when you need remote archives with a pinned version, and treat ADD as a specialized tool you deploy deliberately — not the default when COPY feels like too much typing.